import * as child_process from 'child_process' ; import { PythonPathResult } from '../analyzer/pythonPathUtils' ; import { PythonPlatform } from './configOptions' ; import { assertNever } from './debug' ; import { FileSystem } from './fileSystem' ; import { HostKind , NoAccessHost } from './host' ; import { isDirectory , normalizePath } from './pathUtils' ; import { PythonVersion , versionFromMajorMinor } from './pythonVersion' ; const removeCwdFromSysPath = [ 'import os, os.path, sys' , 'normalize = lambda p: os.path.normcase(os.path.normpath(p))' , 'cwd = normalize(os.getcwd())' , 'orig_sys_path = [p for p in sys.path if p != ""]' , 'sys.path[:] = [p for p in sys.path if p != "" and normalize(p) != cwd]' , ] ; const extractSys = [ ... removeCwdFromSysPath , 'import sys, json' , 'json.dump(dict(path=orig_sys_path, prefix=sys.prefix), sys.stdout)' , ] . join ( '; ' ) ; const extractVersion = [ ... removeCwdFromSysPath , 'import sys, json' , 'json.dump(dict(major=sys.version_info[0], minor=sys.version_info[1]), sys.stdout)' , ] . join ( '; ' ) ; export class LimitedAccessHost extends NoAccessHost { override get kind ( ) : HostKind { return HostKind . LimitedAccess ; } override getPythonPlatform ( logInfo ? ) { if ( process . platform === 'darwin' ) { return PythonPlatform . Darwin ; } else if ( process . platform === 'linux' ) { return PythonPlatform . Linux ; } else if ( process . platform === 'win32' ) { return PythonPlatform . Windows ; } return undefined ; } } export class FullAccessHost extends LimitedAccessHost { static createHost ( kind , fs ) { switch ( kind ) { case HostKind . NoAccess : return new NoAccessHost ( ) ; case HostKind . LimitedAccess : return new LimitedAccessHost ( ) ; case HostKind . FullAccess : return new FullAccessHost ( fs ) ; default : assertNever ( kind ) ; } } constructor ( protected _fs ) { super ( ) ; } override get kind ( ) : HostKind { return HostKind . FullAccess ; } override getPythonSearchPaths ( pythonPath ? , logInfo ? ) { const importFailureInfo = logInfo ?? [ ] ; let result = this . _executePythonInterpreter ( pythonPath , ( p ) => this . _getSearchPathResultFromInterpreter ( this . _fs , p , importFailureInfo ) ) ; if ( ! result ) { result = { paths : [ ] , prefix : '' , } ; } importFailureInfo . push ( `Received ${ result . paths . length } paths from interpreter` ) ; result . paths . forEach ( ( path ) => { importFailureInfo . push ( `  ${ path }` ) ; } ) ; return result ; } override getPythonVersion ( pythonPath ? , logInfo ? ) { const importFailureInfo = logInfo ?? [ ] ; try { const commandLineArgs = [ '-c' , extractVersion ] ; const execOutput = this . _executePythonInterpreter ( pythonPath , ( p ) => child_process . execFileSync ( p , commandLineArgs , { encoding : 'utf8' } ) ) ; const versionJson = JSON . parse ( execOutput ! ) ; const version = versionFromMajorMinor ( versionJson . major , versionJson . minor ) ; if ( version === undefined ) { importFailureInfo . push ( `Python version ${ versionJson . major }.${ versionJson . minor } from interpreter is unsupported` ) ; return undefined ; } return version ; } catch { importFailureInfo . push ( 'Unable to get Python version from interpreter' ) ; return undefined ; } } private _executePythonInterpreter < T > ( pythonPath , execute ) { if ( pythonPath ) { return execute ( pythonPath ) ; } else { let result ; try { if ( process . platform !== 'win32' ) { result = execute ( 'python3' ) ; } } catch { } if ( result !== undefined ) { return result ; } return execute ( 'python' ) ; } } private _getSearchPathResultFromInterpreter ( fs , interpreter , importFailureInfo ) { const result = { paths : [ ] , prefix : '' , } ; try { const commandLineArgs = [ '-c' , extractSys ] ; importFailureInfo . push ( `Executing interpreter: '${ interpreter }'` ) ; const execOutput = child_process . execFileSync ( interpreter , commandLineArgs , { encoding : 'utf8' } ) ; try { const execSplit = JSON . parse ( execOutput ) ; for ( let execSplitEntry of execSplit . path ) { execSplitEntry = execSplitEntry . trim ( ) ; if ( execSplitEntry ) { const normalizedPath = normalizePath ( execSplitEntry ) ; if ( fs . existsSync ( normalizedPath ) && isDirectory ( fs , normalizedPath ) ) { result . paths . push ( normalizedPath ) ; } else { importFailureInfo . push ( `Skipping '${ normalizedPath }' because it is not a valid directory` ) ; } } } result . prefix = execSplit . prefix ; if ( result . paths . length === 0 ) { importFailureInfo . push ( `Found no valid directories` ) ; } } catch ( err ) { importFailureInfo . push ( `Could not parse output: '${ execOutput }'` ) ; throw err ; } } catch { return undefined ; } return result ; } }