import { ConfigOptions } from '../common/configOptions' ; import { compareComparableValues } from '../common/core' ; import { FileSystem } from '../common/fileSystem' ; import { Host } from '../common/host' ; import * as pathConsts from '../common/pathConsts' ; import { combinePaths , containsPath , ensureTrailingDirectorySeparator , getDirectoryPath , getFileSystemEntries , isDirectory , normalizePath , tryStat , } from '../common/pathUtils' ; export interface PythonPathResult { paths : string [ ] ; prefix : string ; } export const stdLibFolderName = 'stdlib' ; export const thirdPartyFolderName = 'stubs' ; export function getTypeShedFallbackPath ( fs ) { let moduleDirectory = fs . getModulePath ( ) ; if ( ! moduleDirectory ) { return undefined ; } moduleDirectory = getDirectoryPath ( ensureTrailingDirectorySeparator ( normalizePath ( moduleDirectory ) ) ) ; const typeshedPath = combinePaths ( moduleDirectory , pathConsts . typeshedFallback ) ; if ( fs . existsSync ( typeshedPath ) ) { return typeshedPath ; } const debugTypeshedPath = combinePaths ( getDirectoryPath ( moduleDirectory ) , pathConsts . typeshedFallback ) ; if ( fs . existsSync ( debugTypeshedPath ) ) { return debugTypeshedPath ; } return undefined ; } export function getTypeshedSubdirectory ( typeshedPath , isStdLib ) { return combinePaths ( typeshedPath , isStdLib ? stdLibFolderName : thirdPartyFolderName ) ; } export function findPythonSearchPaths ( fs , configOptions , host , importFailureInfo , includeWatchPathsOnly ? , workspaceRoot ? ) { importFailureInfo . push ( 'Finding python search paths' ) ; if ( configOptions . venvPath !== undefined && configOptions . venv ) { const venvDir = configOptions . venv ; const venvPath = combinePaths ( configOptions . venvPath , venvDir ) ; const foundPaths = [ ] ; const sitePackagesPaths = [ ] ; [ pathConsts . lib , pathConsts . lib64 , pathConsts . libAlternate ] . forEach ( ( libPath ) => { const sitePackagesPath = findSitePackagesPath ( fs , combinePaths ( venvPath , libPath ) , importFailureInfo ) ; if ( sitePackagesPath ) { addPathIfUnique ( foundPaths , sitePackagesPath ) ; sitePackagesPaths . push ( sitePackagesPath ) ; } } ) ; sitePackagesPaths . forEach ( ( sitePackagesPath ) => { const pthPaths = getPathsFromPthFiles ( fs , sitePackagesPath ) ; pthPaths . forEach ( ( path ) => { addPathIfUnique ( foundPaths , path ) ; } ) ; } ) ; if ( foundPaths . length > 0 ) { importFailureInfo . push ( `Found the following '${ pathConsts . sitePackages }' dirs` ) ; foundPaths . forEach ( ( path ) => { importFailureInfo . push ( `  ${ path }` ) ; } ) ; return foundPaths ; } importFailureInfo . push ( `Did not find any '${ pathConsts . sitePackages }' dirs. Falling back on python interpreter.` ) ; } const pathResult = host . getPythonSearchPaths ( configOptions . pythonPath , importFailureInfo ) ; if ( includeWatchPathsOnly && workspaceRoot ) { const paths = pathResult . paths . filter ( ( p ) => ! containsPath ( workspaceRoot , p , true ) || containsPath ( pathResult . prefix , p , true ) ) ; return paths ; } return pathResult . paths ; } export function isPythonBinary ( p ) { p = p . trim ( ) ; return p === 'python' || p === 'python3' ; } function findSitePackagesPath ( fs , libPath , importFailureInfo ) { if ( fs . existsSync ( libPath ) ) { importFailureInfo . push ( `Found path '${ libPath }'; looking for ${ pathConsts . sitePackages }` ) ; } else { importFailureInfo . push ( `Did not find '${ libPath }'` ) ; return undefined ; } const sitePackagesPath = combinePaths ( libPath , pathConsts . sitePackages ) ; if ( fs . existsSync ( sitePackagesPath ) ) { importFailureInfo . push ( `Found path '${ sitePackagesPath }'` ) ; return sitePackagesPath ; } else { importFailureInfo . push ( `Did not find '${ sitePackagesPath }', so looking for python subdirectory` ) ; } const entries = getFileSystemEntries ( fs , libPath ) ; for ( let i = 0 ; i < entries . directories . length ; i ++ ) { const dirName = entries . directories [ i ] ; if ( dirName . startsWith ( 'python' ) ) { const dirPath = combinePaths ( libPath , dirName , pathConsts . sitePackages ) ; if ( fs . existsSync ( dirPath ) ) { importFailureInfo . push ( `Found path '${ dirPath }'` ) ; return dirPath ; } else { importFailureInfo . push ( `Path '${ dirPath }' is not a valid directory` ) ; } } } return undefined ; } export function getPathsFromPthFiles ( fs , parentDir ) { const searchPaths = [ ] ; const pthFiles = fs . readdirEntriesSync ( parentDir ) . filter ( ( entry ) => ( entry . isFile ( ) || entry . isSymbolicLink ( ) ) && entry . name . endsWith ( '.pth' ) ) . sort ( ( a , b ) => compareComparableValues ( a . name , b . name ) ) ; pthFiles . forEach ( ( pthFile ) => { const filePath = combinePaths ( parentDir , pthFile . name ) ; const fileStats = tryStat ( fs , filePath ) ; if ( fileStats ?. isFile ( ) && fileStats . size > 0 && fileStats . size < 64 * 1024 ) { const data = fs . readFileSync ( filePath , 'utf8' ) ; const lines = data . split ( /\r?\n/ ) ; lines . forEach ( ( line ) => { const trimmedLine = line . trim ( ) ; if ( trimmedLine . length > 0 && ! trimmedLine . startsWith ( '#' ) && ! trimmedLine . match ( /^import\s/ ) ) { const pthPath = combinePaths ( parentDir , trimmedLine ) ; if ( fs . existsSync ( pthPath ) && isDirectory ( fs , pthPath ) ) { searchPaths . push ( pthPath ) ; } } } ) ; } } ) ; return searchPaths ; } function addPathIfUnique ( pathList , pathToAdd ) { if ( ! pathList . some ( ( path ) => path === pathToAdd ) ) { pathList . push ( pathToAdd ) ; return true ; } return false ; }