import type { Dirent } from 'fs' ; import { flatten , getMapValues , getOrAdd } from '../common/collectionUtils' ; import { ConfigOptions , ExecutionEnvironment } from '../common/configOptions' ; import { FileSystem } from '../common/fileSystem' ; import { Host } from '../common/host' ; import { stubsSuffix } from '../common/pathConsts' ; import { changeAnyExtension , combinePathComponents , combinePaths , containsPath , ensureTrailingDirectorySeparator , getDirectoryPath , getFileExtension , getFileName , getFileSystemEntriesFromDirEntries , getPathComponents , getRelativePathComponentsFromDirectory , isDirectory , isDiskPathRoot , isFile , normalizePath , normalizePathCase , resolvePaths , stripFileExtension , stripTrailingDirectorySeparator , tryRealpath , tryStat , } from '../common/pathUtils' ; import { PythonVersion , versionFromString } from '../common/pythonVersion' ; import { equateStringsCaseInsensitive } from '../common/stringUtils' ; import * as StringUtils from '../common/stringUtils' ; import { isIdentifierChar , isIdentifierStartChar } from '../parser/characters' ; import { PyrightFileSystem } from '../pyrightFileSystem' ; import { ImplicitImport , ImportResult , ImportType } from './importResult' ; import { getDirectoryLeadingDotsPointsTo } from './importStatementUtils' ; import { ImportPath , ParentDirectoryCache } from './parentDirectoryCache' ; import * as PythonPathUtils from './pythonPathUtils' ; import { getPyTypedInfo , PyTypedInfo } from './pyTypedUtils' ; import { isDunderName } from './symbolNameUtils' ; export interface ImportedModuleDescriptor { leadingDots : number ; nameParts : string [ ] ; hasTrailingDot ? : boolean | undefined ; importedSymbols : string [ ] | undefined ; } export interface ModuleNameAndType { moduleName : string ; importType : ImportType ; isLocalTypingsFile : boolean ; } export function createImportedModuleDescriptor ( moduleName ) { return { leadingDots : 0 , nameParts : moduleName . split ( '.' ) , importedSymbols : [ ] , } ; } type CachedImportResults = Map < string , ImportResult > ; interface SupportedVersionRange { min : PythonVersion ; max ? : PythonVersion | undefined ; } const supportedNativeLibExtensions = [ '.pyd' , '.so' , '.dylib' ] ; export const supportedFileExtensions = [ '.py' , '.pyi' , ... supportedNativeLibExtensions ] ; const allowPartialResolutionForThirdPartyPackages = false ; export class ImportResolver { private _cachedPythonSearchPaths : string [ ] | undefined ; private _cachedImportResults = new Map < string | undefined , CachedImportResults > ( ) ; private _cachedModuleNameResults = new Map < string , Map < string , ModuleNameAndType > > ( ) ; private _cachedTypeshedRoot : string | undefined ; private _cachedTypeshedStdLibPath : string | undefined ; private _cachedTypeshedStdLibModuleVersions : Map < string , SupportedVersionRange > | undefined ; private _cachedTypeshedThirdPartyPath : string | undefined ; private _cachedTypeshedThirdPartyPackagePaths : Map < string , string [ ] > | undefined ; private _cachedTypeshedThirdPartyPackageRoots : string [ ] | undefined ; private _cachedEntriesForPath = new Map < string , Dirent [ ] > ( ) ; protected cachedParentImportResults : ParentDirectoryCache ; constructor ( public readonly fileSystem , protected _configOptions , public readonly host ) { this . cachedParentImportResults = new ParentDirectoryCache ( ( ) => this . getPythonSearchPaths ( [ ] ) ) ; } invalidateCache ( ) { this . _cachedImportResults = new Map < string | undefined , CachedImportResults > ( ) ; this . _cachedModuleNameResults = new Map < string , Map < string , ModuleNameAndType > > ( ) ; this . cachedParentImportResults . reset ( ) ; this . _invalidateFileSystemCache ( ) ; if ( this . fileSystem instanceof PyrightFileSystem ) { this . fileSystem . clearPartialStubs ( ) ; } } resolveImport ( sourceFilePath , execEnv , moduleDescriptor ) { return this . _resolveImport ( sourceFilePath , execEnv , moduleDescriptor ) ; } protected _resolveImport ( sourceFilePath , execEnv , moduleDescriptor ) { const importName = this . formatImportName ( moduleDescriptor ) ; const importFailureInfo = [ ] ; const importResult = this . _resolveImportStrict ( importName , sourceFilePath , execEnv , moduleDescriptor , importFailureInfo ) ; if ( importResult . isImportFound || moduleDescriptor . leadingDots > 0 ) { return importResult ; } sourceFilePath = normalizePathCase ( this . fileSystem , normalizePath ( sourceFilePath ) ) ; const origin = ensureTrailingDirectorySeparator ( getDirectoryPath ( sourceFilePath ) ) ; const result = this . cachedParentImportResults . getImportResult ( origin , importName , importResult ) ; if ( result ) { return this . filterImplicitImports ( result , moduleDescriptor . importedSymbols ) ; } const root = this . getParentImportResolutionRoot ( sourceFilePath , execEnv . root ) ; if ( ! this . cachedParentImportResults . checkValidPath ( this . fileSystem , sourceFilePath , root ) ) { return importResult ; } const importPath = { importPath : undefined } ; let current = origin ; while ( this . _shouldWalkUp ( current , root , execEnv ) ) { const result = this . resolveAbsoluteImport ( current , execEnv , moduleDescriptor , importName , [ ] , undefined , undefined , false , true ) ; this . cachedParentImportResults . checked ( current , importName , importPath ) ; if ( result . isImportFound ) { importPath . importPath = current ; this . cachedParentImportResults . add ( { importResult : result , path : current , importName , } ) ; return this . filterImplicitImports ( result , moduleDescriptor . importedSymbols ) ; } let success ; [ success , current ] = this . _tryWalkUp ( current ) ; if ( ! success ) { break ; } } this . cachedParentImportResults . checked ( current , importName , importPath ) ; return importResult ; } private _resolveImportStrict ( importName , sourceFilePath , execEnv , moduleDescriptor , importFailureInfo ) { const notFoundResult = { importName , isRelative : false , isImportFound : false , isPartlyResolved : false , isNamespacePackage : false , isStubPackage : false , importFailureInfo , resolvedPaths : [ ] , importType : ImportType . Local , isStubFile : false , isNativeLib : false , implicitImports : [ ] , filteredImplicitImports : [ ] , nonStubImportResult : undefined , } ; this . ensurePartialStubPackages ( execEnv ) ; if ( moduleDescriptor . leadingDots > 0 ) { const relativeImport = this . _resolveRelativeImport ( sourceFilePath , execEnv , moduleDescriptor , importName , importFailureInfo ) ; if ( relativeImport ) { relativeImport . isRelative = true ; return relativeImport ; } } else { const cachedResults = this . _lookUpResultsInCache ( execEnv , importName , moduleDescriptor . importedSymbols ) ; if ( cachedResults ) { const isUnresolvedNamespace = cachedResults . isImportFound && cachedResults . isNamespacePackage && ! this . _isNamespacePackageResolved ( moduleDescriptor , cachedResults . implicitImports ) ; if ( ! isUnresolvedNamespace ) { return cachedResults ; } } const bestImport = this . _resolveBestAbsoluteImport ( sourceFilePath , execEnv , moduleDescriptor , true ) ; if ( bestImport ) { if ( bestImport . isStubFile ) { bestImport . nonStubImportResult = this . _resolveBestAbsoluteImport ( sourceFilePath , execEnv , moduleDescriptor , false ) || notFoundResult ; } return this . addResultsToCache ( execEnv , importName , bestImport , moduleDescriptor . importedSymbols ) ; } } return this . addResultsToCache ( execEnv , importName , notFoundResult , undefined ) ; } getCompletionSuggestions ( sourceFilePath , execEnv , moduleDescriptor ) { const suggestions = this . _getCompletionSuggestionsStrict ( sourceFilePath , execEnv , moduleDescriptor ) ; if ( moduleDescriptor . leadingDots > 0 ) { return suggestions ; } const root = this . getParentImportResolutionRoot ( sourceFilePath , execEnv . root ) ; const origin = ensureTrailingDirectorySeparator ( getDirectoryPath ( normalizePathCase ( this . fileSystem , normalizePath ( sourceFilePath ) ) ) ) ; let current = origin ; while ( this . _shouldWalkUp ( current , root , execEnv ) ) { this . _getCompletionSuggestionsAbsolute ( sourceFilePath , execEnv , current , moduleDescriptor , suggestions , false ) ; let success ; [ success , current ] = this . _tryWalkUp ( current ) ; if ( ! success ) { break ; } } return suggestions ; } private _getCompletionSuggestionsStrict ( sourceFilePath , execEnv , moduleDescriptor ) { const importFailureInfo = [ ] ; const suggestions = new Set < string > ( ) ; if ( moduleDescriptor . leadingDots > 0 ) { this . _getCompletionSuggestionsRelative ( sourceFilePath , execEnv , moduleDescriptor , suggestions ) ; } else { if ( moduleDescriptor . nameParts . length > 0 ) { this . _getCompletionSuggestionsTypeshedPath ( sourceFilePath , execEnv , moduleDescriptor , true , suggestions ) ; } if ( execEnv . root ) { this . _getCompletionSuggestionsAbsolute ( sourceFilePath , execEnv , execEnv . root , moduleDescriptor , suggestions ) ; } for ( const extraPath of execEnv . extraPaths ) { this . _getCompletionSuggestionsAbsolute ( sourceFilePath , execEnv , extraPath , moduleDescriptor , suggestions ) ; } if ( this . _configOptions . stubPath ) { this . _getCompletionSuggestionsAbsolute ( sourceFilePath , execEnv , this . _configOptions . stubPath , moduleDescriptor , suggestions ) ; } this . _getCompletionSuggestionsTypeshedPath ( sourceFilePath , execEnv , moduleDescriptor , false , suggestions ) ; const pythonSearchPaths = this . getPythonSearchPaths ( importFailureInfo ) ; for ( const searchPath of pythonSearchPaths ) { this . _getCompletionSuggestionsAbsolute ( sourceFilePath , execEnv , searchPath , moduleDescriptor , suggestions ) ; } } return suggestions ; } getSourceFilesFromStub ( stubFilePath , execEnv , _mapCompiled ) { const sourceFilePaths = [ ] ; this . _cachedImportResults . forEach ( ( map ) => { map . forEach ( ( result ) => { if ( result . isStubFile && result . isImportFound && result . nonStubImportResult ) { if ( result . resolvedPaths [ result . resolvedPaths . length - 1 ] === stubFilePath ) { if ( result . nonStubImportResult . isImportFound ) { const nonEmptyPath = result . nonStubImportResult . resolvedPaths [ result . nonStubImportResult . resolvedPaths . length - 1 ] ; if ( nonEmptyPath . endsWith ( '.py' ) ) { sourceFilePaths . push ( nonEmptyPath ) ; } } } } } ) ; } ) ; if ( sourceFilePaths . length === 0 ) { const sourceFilePath = changeAnyExtension ( stubFilePath , '.py' ) ; if ( this . dirExistsCached ( sourceFilePath ) ) { sourceFilePaths . push ( sourceFilePath ) ; } } if ( sourceFilePaths . length === 0 ) { const importRootPaths = this . getImportRoots ( execEnv ) ; const relativeStubPaths = [ ] ; for ( const importRootPath of importRootPaths ) { if ( containsPath ( importRootPath , stubFilePath , true ) ) { const parts = getRelativePathComponentsFromDirectory ( importRootPath , stubFilePath , true ) ; if ( parts . length > 1 ) { if ( parts [ 1 ] . endsWith ( stubsSuffix ) ) { parts [ 1 ] = parts [ 1 ] . substr ( 0 , parts [ 1 ] . length - stubsSuffix . length ) ; } const relativeStubPath = combinePathComponents ( parts ) ; if ( relativeStubPath ) { relativeStubPaths . push ( relativeStubPath ) ; } } } } for ( const relativeStubPath of relativeStubPaths ) { for ( const importRootPath of importRootPaths ) { const absoluteStubPath = resolvePaths ( importRootPath , relativeStubPath ) ; let absoluteSourcePath = changeAnyExtension ( absoluteStubPath , '.py' ) ; if ( this . fileExistsCached ( absoluteSourcePath ) ) { sourceFilePaths . push ( absoluteSourcePath ) ; } else { const filePathWithoutExtension = stripFileExtension ( absoluteSourcePath ) ; if ( filePathWithoutExtension . endsWith ( '__init__' ) ) { absoluteSourcePath = filePathWithoutExtension . substr ( 0 , filePathWithoutExtension . length - 9 ) + '.py' ; if ( this . fileExistsCached ( absoluteSourcePath ) ) { sourceFilePaths . push ( absoluteSourcePath ) ; } } else { absoluteSourcePath = combinePaths ( filePathWithoutExtension , '__init__.py' ) ; if ( this . fileExistsCached ( absoluteSourcePath ) ) { sourceFilePaths . push ( absoluteSourcePath ) ; } } } } } } return sourceFilePaths ; } getModuleNameForImport ( filePath , execEnv ) { const cache = getOrAdd ( this . _cachedModuleNameResults , execEnv . root , ( ) => new Map < string , ModuleNameAndType > ( ) ) ; return getOrAdd ( cache , filePath , ( ) => this . _getModuleNameForImport ( filePath , execEnv ) ) ; } private _getModuleNameForImport ( filePath , execEnv ) { let moduleName ; let importType = ImportType . BuiltIn ; let isLocalTypingsFile = false ; const importFailureInfo = [ ] ; const stdLibTypeshedPath = this . _getStdlibTypeshedPath ( execEnv , importFailureInfo ) ; if ( stdLibTypeshedPath ) { moduleName = this . getModuleNameFromPath ( stdLibTypeshedPath , filePath ) ; if ( moduleName ) { const moduleDescriptor = { leadingDots : 0 , nameParts : moduleName . split ( '.' ) , importedSymbols : undefined , } ; if ( this . _isStdlibTypeshedStubValidForVersion ( moduleDescriptor , execEnv , [ ] ) ) { return { moduleName , importType , isLocalTypingsFile } ; } } } if ( execEnv . root ) { moduleName = this . getModuleNameFromPath ( execEnv . root , filePath ) ; importType = ImportType . Local ; } for ( const extraPath of execEnv . extraPaths ) { const candidateModuleName = this . getModuleNameFromPath ( extraPath , filePath ) ; if ( ! moduleName || ( candidateModuleName && candidateModuleName . length < moduleName . length ) ) { moduleName = candidateModuleName ; importType = ImportType . Local ; } } if ( this . _configOptions . stubPath ) { const candidateModuleName = this . getModuleNameFromPath ( this . _configOptions . stubPath , filePath ) ; if ( ! moduleName || ( candidateModuleName && candidateModuleName . length < moduleName . length ) ) { moduleName = candidateModuleName ; importType = ImportType . Local ; isLocalTypingsFile = true ; } } const thirdPartyTypeshedPath = this . _getThirdPartyTypeshedPath ( execEnv , importFailureInfo ) ; if ( thirdPartyTypeshedPath ) { const candidateModuleName = this . getModuleNameFromPath ( thirdPartyTypeshedPath , filePath , true ) ; if ( ! moduleName || ( candidateModuleName && candidateModuleName . length < moduleName . length ) ) { moduleName = candidateModuleName ; importType = ImportType . ThirdParty ; } } const thirdPartyTypeshedPathEx = this . getTypeshedPathEx ( execEnv , importFailureInfo ) ; if ( thirdPartyTypeshedPathEx ) { const candidateModuleName = this . getModuleNameFromPath ( thirdPartyTypeshedPathEx , filePath ) ; if ( ! moduleName || ( candidateModuleName && candidateModuleName . length < moduleName . length ) ) { moduleName = candidateModuleName ; importType = ImportType . ThirdParty ; } } const pythonSearchPaths = this . getPythonSearchPaths ( importFailureInfo ) ; for ( const searchPath of pythonSearchPaths ) { const candidateModuleName = this . getModuleNameFromPath ( searchPath , filePath ) ; if ( ! moduleName || ( candidateModuleName && candidateModuleName . length < moduleName . length ) ) { moduleName = candidateModuleName ; importType = ImportType . ThirdParty ; } } if ( moduleName ) { return { moduleName , importType , isLocalTypingsFile } ; } return { moduleName : '' , importType : ImportType . Local , isLocalTypingsFile } ; } getTypeshedStdLibPath ( execEnv ) { const unused = [ ] ; return this . _getStdlibTypeshedPath ( execEnv , unused ) ; } getImportRoots ( execEnv , forLogging = false ) { const importFailureInfo = [ ] ; const roots = [ ] ; const stdTypeshed = this . _getStdlibTypeshedPath ( execEnv , importFailureInfo ) ; if ( stdTypeshed ) { roots . push ( stdTypeshed ) ; } if ( execEnv . root ) { roots . push ( execEnv . root ) ; } roots . push ( ... execEnv . extraPaths ) ; if ( this . _configOptions . stubPath ) { roots . push ( this . _configOptions . stubPath ) ; } if ( forLogging ) { const thirdPartyRoot = this . _getThirdPartyTypeshedPath ( execEnv , importFailureInfo ) ; if ( thirdPartyRoot ) { roots . push ( combinePaths ( thirdPartyRoot , '...' ) ) ; } } else { const thirdPartyPaths = this . _getThirdPartyTypeshedPackageRoots ( execEnv , importFailureInfo ) ; roots . push ( ... thirdPartyPaths ) ; } const typeshedPathEx = this . getTypeshedPathEx ( execEnv , importFailureInfo ) ; if ( typeshedPathEx ) { roots . push ( typeshedPathEx ) ; } const pythonSearchPaths = this . getPythonSearchPaths ( importFailureInfo ) ; if ( pythonSearchPaths . length > 0 ) { roots . push ( ... pythonSearchPaths ) ; } return roots ; } protected readdirEntriesCached ( path ) { const cachedValue = this . _cachedEntriesForPath . get ( path ) ; if ( cachedValue ) { return cachedValue ; } let newCacheValue ; try { newCacheValue = this . fileSystem . readdirEntriesSync ( path ) ; } catch { newCacheValue = [ ] ; } this . _cachedEntriesForPath . set ( path , newCacheValue ) ; return newCacheValue ; } protected fileExistsCached ( path ) { const splitPath = this . _splitPath ( path ) ; if ( ! splitPath [ 0 ] || ! splitPath [ 1 ] ) { if ( ! this . fileSystem . existsSync ( path ) ) { return false ; } return tryStat ( this . fileSystem , path ) ?. isFile ( ) ?? false ; } const entries = this . readdirEntriesCached ( splitPath [ 0 ] ) ; const entry = entries . find ( ( entry ) => entry . name === splitPath [ 1 ] ) ; if ( entry ?. isFile ( ) ) { return true ; } if ( entry ?. isSymbolicLink ( ) ) { const realPath = tryRealpath ( this . fileSystem , path ) ; if ( realPath && this . fileSystem . existsSync ( realPath ) && isFile ( this . fileSystem , realPath ) ) { return true ; } } return false ; } protected dirExistsCached ( path ) { const splitPath = this . _splitPath ( path ) ; if ( ! splitPath [ 0 ] || ! splitPath [ 1 ] ) { if ( ! this . fileSystem . existsSync ( path ) ) { return false ; } return tryStat ( this . fileSystem , path ) ?. isDirectory ( ) ?? false ; } const entries = this . readdirEntriesCached ( splitPath [ 0 ] ) ; const entry = entries . find ( ( entry ) => entry . name === splitPath [ 1 ] ) ; if ( entry ?. isDirectory ( ) ) { return true ; } if ( entry ?. isSymbolicLink ( ) ) { const realPath = tryRealpath ( this . fileSystem , path ) ; if ( realPath && this . fileSystem . existsSync ( realPath ) && isDirectory ( this . fileSystem , realPath ) ) { return true ; } } return false ; } ensurePartialStubPackages ( execEnv ) { if ( ! ( this . fileSystem instanceof PyrightFileSystem ) ) { return false ; } if ( this . fileSystem . isPartialStubPackagesScanned ( execEnv ) ) { return false ; } const fs = this . fileSystem ; const ignored = [ ] ; const paths = [ ] ; addPaths ( this . _configOptions . stubPath ) ; addPaths ( execEnv . root ) ; execEnv . extraPaths . forEach ( ( p ) => addPaths ( p ) ) ; addPaths ( this . getTypeshedPathEx ( execEnv , ignored ) ) ; this . getPythonSearchPaths ( ignored ) . forEach ( ( p ) => addPaths ( p ) ) ; this . fileSystem . processPartialStubPackages ( paths , this . getImportRoots ( execEnv ) ) ; this . _invalidateFileSystemCache ( ) ; return true ; function addPaths ( path ? ) { if ( ! path || fs . isPathScanned ( path ) ) { return ; } paths . push ( path ) ; } } protected addResultsToCache ( execEnv , importName , importResult , importedSymbols ) { getOrAdd ( this . _cachedImportResults , execEnv . root , ( ) => new Map < string , ImportResult > ( ) ) . set ( importName , importResult ) ; return this . filterImplicitImports ( importResult , importedSymbols ) ; } protected resolveAbsoluteImport ( rootPath , execEnv , moduleDescriptor , importName , importFailureInfo , allowPartial = false , allowNativeLib = false , useStubPackage = false , allowPyi = true , lookForPyTyped = false ) { if ( allowPyi && useStubPackage ) { const importResult = this . _resolveAbsoluteImport ( rootPath , execEnv , moduleDescriptor , importName , importFailureInfo , allowPartial , false , true , true , true ) ; if ( importResult . packageDirectory ) { if ( ! importResult . isNamespacePackage || importResult . isImportFound ) { return importResult ; } } } return this . _resolveAbsoluteImport ( rootPath , execEnv , moduleDescriptor , importName , importFailureInfo , allowPartial , allowNativeLib , false , allowPyi , lookForPyTyped ) ; } private _invalidateFileSystemCache ( ) { this . _cachedEntriesForPath . clear ( ) ; } private _splitPath ( path ) { const pathComponents = getPathComponents ( path ) ; if ( pathComponents . length <= 1 ) { return [ path , '' ] ; } const containingPath = combinePathComponents ( pathComponents . slice ( 0 , - 1 ) ) ; const fileOrDirName = pathComponents [ pathComponents . length - 1 ] ; return [ containingPath , fileOrDirName ] ; } private _resolveAbsoluteImport ( rootPath , execEnv , moduleDescriptor , importName , importFailureInfo , allowPartial , allowNativeLib , useStubPackage , allowPyi , lookForPyTyped ) { if ( useStubPackage ) { importFailureInfo . push ( `Attempting to resolve stub package using root path '${ rootPath }'` ) ; } else { importFailureInfo . push ( `Attempting to resolve using root path '${ rootPath }'` ) ; } const resolvedPaths = [ ] ; let dirPath = rootPath ; let isNamespacePackage = false ; let isStubPackage = false ; let isStubFile = false ; let isNativeLib = false ; let implicitImports = [ ] ; let packageDirectory ; let pyTypedInfo ; if ( moduleDescriptor . nameParts . length === 0 ) { const fileNameWithoutExtension = '__init__' ; const pyFilePath = combinePaths ( dirPath , fileNameWithoutExtension + '.py' ) ; const pyiFilePath = combinePaths ( dirPath , fileNameWithoutExtension + '.pyi' ) ; if ( allowPyi && this . fileExistsCached ( pyiFilePath ) ) { importFailureInfo . push ( `Resolved import with file '${ pyiFilePath }'` ) ; resolvedPaths . push ( pyiFilePath ) ; isStubFile = true ; } else if ( this . fileExistsCached ( pyFilePath ) ) { importFailureInfo . push ( `Resolved import with file '${ pyFilePath }'` ) ; resolvedPaths . push ( pyFilePath ) ; } else { importFailureInfo . push ( `Partially resolved import with directory '${ dirPath }'` ) ; resolvedPaths . push ( '' ) ; isNamespacePackage = true ; } implicitImports = this . _findImplicitImports ( importName , dirPath , [ pyFilePath , pyiFilePath ] ) ; } else { for ( let i = 0 ; i < moduleDescriptor . nameParts . length ; i ++ ) { const isFirstPart = i === 0 ; const isLastPart = i === moduleDescriptor . nameParts . length - 1 ; dirPath = combinePaths ( dirPath , moduleDescriptor . nameParts [ i ] ) ; if ( useStubPackage && isFirstPart ) { dirPath += stubsSuffix ; isStubPackage = true ; } const foundDirectory = this . dirExistsCached ( dirPath ) ; if ( foundDirectory ) { if ( isFirstPart ) { packageDirectory = dirPath ; } const fileNameWithoutExtension = '__init__' ; const pyFilePath = combinePaths ( dirPath , fileNameWithoutExtension + '.py' ) ; const pyiFilePath = combinePaths ( dirPath , fileNameWithoutExtension + '.pyi' ) ; let foundInit = false ; if ( allowPyi && this . fileExistsCached ( pyiFilePath ) ) { importFailureInfo . push ( `Resolved import with file '${ pyiFilePath }'` ) ; resolvedPaths . push ( pyiFilePath ) ; if ( isLastPart ) { isStubFile = true ; } foundInit = true ; } else if ( this . fileExistsCached ( pyFilePath ) ) { importFailureInfo . push ( `Resolved import with file '${ pyFilePath }'` ) ; resolvedPaths . push ( pyFilePath ) ; foundInit = true ; } if ( foundInit && ! pyTypedInfo && lookForPyTyped ) { if ( this . fileExistsCached ( combinePaths ( dirPath , 'py.typed' ) ) ) { pyTypedInfo = getPyTypedInfo ( this . fileSystem , dirPath ) ; } } if ( ! isLastPart ) { if ( ! foundInit ) { resolvedPaths . push ( '' ) ; isNamespacePackage = true ; pyTypedInfo = undefined ; } continue ; } if ( foundInit ) { implicitImports = this . _findImplicitImports ( moduleDescriptor . nameParts . join ( '.' ) , dirPath , [ pyFilePath , pyiFilePath , ] ) ; break ; } } let fileDirectory = stripTrailingDirectorySeparator ( dirPath ) ; const fileNameWithoutExtension = getFileName ( fileDirectory ) ; fileDirectory = getDirectoryPath ( fileDirectory ) ; const pyFilePath = combinePaths ( fileDirectory , fileNameWithoutExtension + '.py' ) ; const pyiFilePath = combinePaths ( fileDirectory , fileNameWithoutExtension + '.pyi' ) ; if ( allowPyi && this . fileExistsCached ( pyiFilePath ) ) { importFailureInfo . push ( `Resolved import with file '${ pyiFilePath }'` ) ; resolvedPaths . push ( pyiFilePath ) ; if ( isLastPart ) { isStubFile = true ; } } else if ( this . fileExistsCached ( pyFilePath ) ) { importFailureInfo . push ( `Resolved import with file '${ pyFilePath }'` ) ; resolvedPaths . push ( pyFilePath ) ; } else { if ( allowNativeLib && this . dirExistsCached ( fileDirectory ) ) { const filesInDir = this . _getFilesInDirectory ( fileDirectory ) ; const nativeLibFileName = filesInDir . find ( ( f ) => this . _isNativeModuleFileName ( fileNameWithoutExtension , f ) ) ; if ( nativeLibFileName ) { const nativeLibPath = combinePaths ( fileDirectory , nativeLibFileName ) ; isNativeLib = this . _resolveNativeModuleStub ( nativeLibPath , execEnv , importName , moduleDescriptor , importFailureInfo , resolvedPaths ) ; } } if ( ! isNativeLib && foundDirectory ) { importFailureInfo . push ( `Partially resolved import with directory '${ dirPath }'` ) ; resolvedPaths . push ( '' ) ; if ( isLastPart ) { implicitImports = this . _findImplicitImports ( importName , dirPath , [ pyFilePath , pyiFilePath ] ) ; isNamespacePackage = true ; } } else if ( isNativeLib ) { importFailureInfo . push ( `Did not find file '${ pyiFilePath }' or '${ pyFilePath }'` ) ; } } break ; } } let importFound ; const isPartlyResolved = resolvedPaths . length > 0 && resolvedPaths . length < moduleDescriptor . nameParts . length ; if ( allowPartial ) { importFound = resolvedPaths . length > 0 ; } else { importFound = resolvedPaths . length >= moduleDescriptor . nameParts . length ; } return { importName , isRelative : false , isNamespacePackage , isStubPackage , isImportFound : importFound , isPartlyResolved , importFailureInfo , importType : ImportType . Local , resolvedPaths , searchPath : rootPath , isStubFile , isNativeLib , implicitImports , pyTypedInfo , filteredImplicitImports : implicitImports , packageDirectory , } ; } protected getTypeshedPathEx ( execEnv , importFailureInfo ) { return undefined ; } protected resolveImportEx ( sourceFilePath , execEnv , moduleDescriptor , importName , importFailureInfo = [ ] , allowPyi = true ) { return undefined ; } protected resolveNativeImportEx ( libraryFilePath , importName , importFailureInfo = [ ] ) { return undefined ; } protected getNativeModuleName ( fileName ) { const fileExtension = getFileExtension ( fileName , false ) . toLowerCase ( ) ; if ( this . _isNativeModuleFileExtension ( fileExtension ) ) { return stripFileExtension ( stripFileExtension ( fileName ) ) ; } return undefined ; } private _lookUpResultsInCache ( execEnv , importName , importedSymbols ) { const cacheForExecEnv = this . _cachedImportResults . get ( execEnv . root ) ; if ( ! cacheForExecEnv ) { return undefined ; } const cachedEntry = cacheForExecEnv . get ( importName ) ; if ( ! cachedEntry ) { return undefined ; } return this . filterImplicitImports ( cachedEntry , importedSymbols ) ; } private _isNamespacePackageResolved ( moduleDescriptor , implicitImports ) { if ( moduleDescriptor . importedSymbols ) { if ( ! moduleDescriptor . importedSymbols . some ( ( symbol ) => { return implicitImports . some ( ( implicitImport ) => { return implicitImport . name === symbol ; } ) ; } ) ) { return false ; } } else if ( implicitImports . length === 0 ) { return false ; } return true ; } protected getModuleNameFromPath ( containerPath , filePath , stripTopContainerDir = false ) { containerPath = ensureTrailingDirectorySeparator ( containerPath ) ; let filePathWithoutExtension = stripFileExtension ( filePath ) ; if ( this . _isNativeModuleFileExtension ( getFileExtension ( filePath ) ) ) { filePathWithoutExtension = stripFileExtension ( filePathWithoutExtension ) ; } if ( ! filePathWithoutExtension . startsWith ( containerPath ) ) { return undefined ; } if ( filePathWithoutExtension . endsWith ( '__init__' ) ) { filePathWithoutExtension = filePathWithoutExtension . substr ( 0 , filePathWithoutExtension . length - 9 ) ; } const relativeFilePath = filePathWithoutExtension . substr ( containerPath . length ) ; const parts = getPathComponents ( relativeFilePath ) ; parts . shift ( ) ; if ( stripTopContainerDir ) { if ( parts . length === 0 ) { return undefined ; } parts . shift ( ) ; } if ( parts . length === 0 ) { return undefined ; } if ( parts [ 0 ] . endsWith ( stubsSuffix ) ) { parts [ 0 ] = parts [ 0 ] . substr ( 0 , parts [ 0 ] . length - stubsSuffix . length ) ; } if ( parts . some ( ( p ) => ! this . _isIdentifier ( p ) ) ) { return undefined ; } return parts . join ( '.' ) ; } private _resolveBestAbsoluteImport ( sourceFilePath , execEnv , moduleDescriptor , allowPyi ) { const importName = this . formatImportName ( moduleDescriptor ) ; const importFailureInfo = [ ] ; if ( allowPyi && moduleDescriptor . nameParts . length > 0 ) { const builtInImport = this . _findTypeshedPath ( execEnv , moduleDescriptor , importName , true , importFailureInfo ) ; if ( builtInImport ) { builtInImport . isTypeshedFile = true ; return builtInImport ; } } if ( allowPyi ) { if ( this . _configOptions . stubPath ) { importFailureInfo . push ( `Looking in stubPath '${ this . _configOptions . stubPath }'` ) ; const typingsImport = this . resolveAbsoluteImport ( this . _configOptions . stubPath , execEnv , moduleDescriptor , importName , importFailureInfo , undefined , false , true , allowPyi , false ) ; if ( typingsImport . isImportFound ) { typingsImport . importType = ImportType . Local ; typingsImport . isLocalTypingsFile = true ; return typingsImport ; } } } let bestResultSoFar ; let localImport ; if ( execEnv . root ) { importFailureInfo . push ( `Looking in root directory of execution environment ` + `'${ execEnv . root }'` ) ; localImport = this . resolveAbsoluteImport ( execEnv . root , execEnv , moduleDescriptor , importName , importFailureInfo , undefined , true , true , allowPyi , false ) ; bestResultSoFar = localImport ; } for ( const extraPath of execEnv . extraPaths ) { importFailureInfo . push ( `Looking in extraPath '${ extraPath }'` ) ; localImport = this . resolveAbsoluteImport ( extraPath , execEnv , moduleDescriptor , importName , importFailureInfo , undefined , true , true , allowPyi , false ) ; bestResultSoFar = this . _pickBestImport ( bestResultSoFar , localImport , moduleDescriptor ) ; } const pythonSearchPaths = this . getPythonSearchPaths ( importFailureInfo ) ; if ( pythonSearchPaths . length > 0 ) { for ( const searchPath of pythonSearchPaths ) { importFailureInfo . push ( `Looking in python search path '${ searchPath }'` ) ; const thirdPartyImport = this . resolveAbsoluteImport ( searchPath , execEnv , moduleDescriptor , importName , importFailureInfo , allowPartialResolutionForThirdPartyPackages , true , true , allowPyi , true ) ; if ( thirdPartyImport ) { thirdPartyImport . importType = ImportType . ThirdParty ; if ( thirdPartyImport . isImportFound && thirdPartyImport . isStubFile ) { return thirdPartyImport ; } bestResultSoFar = this . _pickBestImport ( bestResultSoFar , thirdPartyImport , moduleDescriptor ) ; } } } else { importFailureInfo . push ( 'No python interpreter search path' ) ; } if ( execEnv . root !== this . _getTypeshedRoot ( execEnv , importFailureInfo ) ) { if ( bestResultSoFar ?. pyTypedInfo && ! bestResultSoFar . isPartlyResolved ) { return bestResultSoFar ; } } const extraResults = this . resolveImportEx ( sourceFilePath , execEnv , moduleDescriptor , importName , importFailureInfo , allowPyi ) ; if ( extraResults !== undefined ) { return extraResults ; } if ( allowPyi ) { importFailureInfo . push ( `Looking for typeshed path` ) ; const typeshedImport = this . _findTypeshedPath ( execEnv , moduleDescriptor , importName , false , importFailureInfo ) ; if ( typeshedImport ) { typeshedImport . isTypeshedFile = true ; bestResultSoFar = this . _pickBestImport ( bestResultSoFar , typeshedImport , moduleDescriptor ) ; } } return bestResultSoFar ; } private _pickBestImport ( bestImportSoFar , newImport , moduleDescriptor ) { if ( ! bestImportSoFar ) { return newImport ; } if ( ! newImport ) { return bestImportSoFar ; } if ( newImport . isImportFound ) { if ( ! bestImportSoFar . isImportFound ) { return newImport ; } if ( bestImportSoFar . isNamespacePackage && ! newImport . isNamespacePackage ) { return newImport ; } if ( bestImportSoFar . importType === ImportType . Local && ! bestImportSoFar . isNamespacePackage ) { return bestImportSoFar ; } if ( bestImportSoFar . isNamespacePackage && newImport . isNamespacePackage && moduleDescriptor . importedSymbols ) { if ( ! this . _isNamespacePackageResolved ( moduleDescriptor , bestImportSoFar . implicitImports ) && this . _isNamespacePackageResolved ( moduleDescriptor , newImport . implicitImports ) ) { return newImport ; } } if ( bestImportSoFar . pyTypedInfo && ! newImport . pyTypedInfo ) { return bestImportSoFar ; } else if ( ! bestImportSoFar . pyTypedInfo && newImport . pyTypedInfo ) { return newImport ; } if ( bestImportSoFar . isStubFile && ! newImport . isStubFile ) { return bestImportSoFar ; } else if ( ! bestImportSoFar . isStubFile && newImport . isStubFile ) { return newImport ; } if ( bestImportSoFar . resolvedPaths . length > newImport . resolvedPaths . length ) { return newImport ; } } else if ( newImport . isPartlyResolved && bestImportSoFar . isNamespacePackage && ! newImport . isNamespacePackage ) { return newImport ; } return bestImportSoFar ; } private _isIdentifier ( value ) { for ( let i = 0 ; i < value . length ; i ++ ) { if ( i === 0 ? ! isIdentifierStartChar ( value . charCodeAt ( i ) ) : ! isIdentifierChar ( value . charCodeAt ( i ) ) ) { return false ; } } return true ; } protected getPythonSearchPaths ( importFailureInfo ) { if ( ! this . _cachedPythonSearchPaths ) { const paths = ( PythonPathUtils . findPythonSearchPaths ( this . fileSystem , this . _configOptions , this . host , importFailureInfo ) || [ ] ) . map ( ( p ) => this . fileSystem . realCasePath ( p ) ) ; this . _cachedPythonSearchPaths = [ ... new Set ( paths ) ] ; } return this . _cachedPythonSearchPaths ; } private _findTypeshedPath ( execEnv , moduleDescriptor , importName , isStdLib , importFailureInfo ) { importFailureInfo . push ( `Looking for typeshed ${ isStdLib ? PythonPathUtils . stdLibFolderName : PythonPathUtils . thirdPartyFolderName } path` ) ; let typeshedPaths ; if ( isStdLib ) { const path = this . _getStdlibTypeshedPath ( execEnv , importFailureInfo , moduleDescriptor ) ; if ( path ) { typeshedPaths = [ path ] ; } } else { typeshedPaths = this . _getThirdPartyTypeshedPackagePaths ( moduleDescriptor , execEnv , importFailureInfo ) ; } if ( typeshedPaths ) { for ( const typeshedPath of typeshedPaths ) { if ( this . dirExistsCached ( typeshedPath ) ) { const importInfo = this . resolveAbsoluteImport ( typeshedPath , execEnv , moduleDescriptor , importName , importFailureInfo ) ; if ( importInfo . isImportFound ) { importInfo . importType = isStdLib ? ImportType . BuiltIn : ImportType . ThirdParty ; return importInfo ; } } } } importFailureInfo . push ( `Typeshed path not found` ) ; return undefined ; } private _buildTypeshedThirdPartyPackageMap ( thirdPartyDir ) { this . _cachedTypeshedThirdPartyPackagePaths = new Map < string , string [ ] > ( ) ; if ( thirdPartyDir ) { this . readdirEntriesCached ( thirdPartyDir ) . forEach ( ( outerEntry ) => { if ( outerEntry . isDirectory ( ) ) { const innerDirPath = combinePaths ( thirdPartyDir , outerEntry . name ) ; this . readdirEntriesCached ( innerDirPath ) . forEach ( ( innerEntry ) => { if ( innerEntry . name === '@python2' ) { return ; } if ( innerEntry . isDirectory ( ) ) { const pathList = this . _cachedTypeshedThirdPartyPackagePaths ! . get ( innerEntry . name ) ; if ( pathList ) { pathList . push ( innerDirPath ) ; } else { this . _cachedTypeshedThirdPartyPackagePaths ! . set ( innerEntry . name , [ innerDirPath ] ) ; } } else if ( innerEntry . isFile ( ) ) { if ( innerEntry . name . endsWith ( '.pyi' ) ) { const strippedFileName = stripFileExtension ( innerEntry . name ) ; const pathList = this . _cachedTypeshedThirdPartyPackagePaths ! . get ( strippedFileName ) ; if ( pathList ) { pathList . push ( innerDirPath ) ; } else { this . _cachedTypeshedThirdPartyPackagePaths ! . set ( strippedFileName , [ innerDirPath ] ) ; } } } } ) ; } } ) ; } this . _cachedTypeshedThirdPartyPackageRoots = [ ... new Set ( ... this . _cachedTypeshedThirdPartyPackagePaths . values ( ) ) , ] . sort ( ) ; } private _getCompletionSuggestionsTypeshedPath ( sourceFilePath , execEnv , moduleDescriptor , isStdLib , suggestions ) { const importFailureInfo = [ ] ; let typeshedPaths ; if ( isStdLib ) { const path = this . _getStdlibTypeshedPath ( execEnv , importFailureInfo , moduleDescriptor ) ; if ( path ) { typeshedPaths = [ path ] ; } } else { typeshedPaths = this . _getThirdPartyTypeshedPackagePaths ( moduleDescriptor , execEnv , importFailureInfo , false ) ; const typeshedPathEx = this . getTypeshedPathEx ( execEnv , importFailureInfo ) ; if ( typeshedPathEx ) { typeshedPaths = typeshedPaths ?? [ ] ; typeshedPaths . push ( typeshedPathEx ) ; } } if ( ! typeshedPaths ) { return ; } typeshedPaths . forEach ( ( typeshedPath ) => { if ( this . dirExistsCached ( typeshedPath ) ) { this . _getCompletionSuggestionsAbsolute ( sourceFilePath , execEnv , typeshedPath , moduleDescriptor , suggestions ) ; } } ) ; } private _getStdlibTypeshedPath ( execEnv , importFailureInfo , moduleDescriptor ? ) { const subdirectory = this . _getTypeshedSubdirectory ( true , execEnv , importFailureInfo ) ; if ( subdirectory && moduleDescriptor && ! this . _isStdlibTypeshedStubValidForVersion ( moduleDescriptor , execEnv , importFailureInfo ) ) { return undefined ; } return subdirectory ; } private _getThirdPartyTypeshedPath ( execEnv , importFailureInfo ) { return this . _getTypeshedSubdirectory ( false , execEnv , importFailureInfo ) ; } private _isStdlibTypeshedStubValidForVersion ( moduleDescriptor , execEnv , importFailureInfo ) { if ( ! this . _cachedTypeshedStdLibModuleVersions ) { this . _cachedTypeshedStdLibModuleVersions = this . _readTypeshedStdLibVersions ( execEnv , importFailureInfo ) ; } for ( let namePartCount = 1 ; namePartCount <= moduleDescriptor . nameParts . length ; namePartCount ++ ) { const namePartsToConsider = moduleDescriptor . nameParts . slice ( 0 , namePartCount ) ; const versionRange = this . _cachedTypeshedStdLibModuleVersions . get ( namePartsToConsider . join ( '.' ) ) ; if ( versionRange ) { if ( execEnv . pythonVersion < versionRange . min ) { return false ; } if ( versionRange . max !== undefined && execEnv . pythonVersion > versionRange . max ) { return false ; } } } return true ; } private _readTypeshedStdLibVersions ( execEnv , importFailureInfo ) { const versionRangeMap = new Map < string , SupportedVersionRange > ( ) ; const typeshedStdLibPath = this . _getTypeshedSubdirectory ( true , execEnv , importFailureInfo ) ; if ( typeshedStdLibPath ) { const versionsFilePath = combinePaths ( typeshedStdLibPath , 'VERSIONS' ) ; try { const fileStats = this . fileSystem . statSync ( versionsFilePath ) ; if ( fileStats . size > 0 && fileStats . size < 256 * 1024 ) { const fileContents = this . fileSystem . readFileSync ( versionsFilePath , 'utf8' ) ; fileContents . split ( /\r?\n/ ) . forEach ( ( line ) => { const commentSplit = line . split ( '#' ) ; const colonSplit = commentSplit [ 0 ] . split ( ':' ) ; if ( colonSplit . length !== 2 ) { return ; } const versionSplit = colonSplit [ 1 ] . split ( '-' ) ; if ( versionSplit . length > 2 ) { return ; } const moduleName = colonSplit [ 0 ] . trim ( ) ; if ( ! moduleName ) { return ; } let minVersionString = versionSplit [ 0 ] . trim ( ) ; if ( minVersionString . endsWith ( '+' ) ) { minVersionString = minVersionString . substr ( 0 , minVersionString . length - 1 ) ; } let minVersion = versionFromString ( minVersionString ) ; if ( ! minVersion ) { minVersion = PythonVersion . V3_0 ; } let maxVersion ; if ( versionSplit . length > 1 ) { maxVersion = versionFromString ( versionSplit [ 1 ] . trim ( ) ) ; } versionRangeMap . set ( moduleName , { min : minVersion , max : maxVersion } ) ; } ) ; } else { importFailureInfo . push ( `Typeshed stdlib VERSIONS file is unexpectedly large` ) ; } } catch ( e ) { importFailureInfo . push ( `Could not read typeshed stdlib VERSIONS file: '${ JSON . stringify ( e ) }'` ) ; } } return versionRangeMap ; } private _getThirdPartyTypeshedPackagePaths ( moduleDescriptor , execEnv , importFailureInfo , includeMatchOnly = true ) { const typeshedPath = this . _getThirdPartyTypeshedPath ( execEnv , importFailureInfo ) ; if ( ! this . _cachedTypeshedThirdPartyPackagePaths ) { this . _buildTypeshedThirdPartyPackageMap ( typeshedPath ) ; } const firstNamePart = moduleDescriptor . nameParts . length > 0 ? moduleDescriptor . nameParts [ 0 ] : '' ; if ( includeMatchOnly ) { return this . _cachedTypeshedThirdPartyPackagePaths ! . get ( firstNamePart ) ; } if ( firstNamePart ) { return flatten ( getMapValues ( this . _cachedTypeshedThirdPartyPackagePaths ! , ( k ) => k . startsWith ( firstNamePart ) ) ) ; } return [ ] ; } private _getThirdPartyTypeshedPackageRoots ( execEnv , importFailureInfo ) { const typeshedPath = this . _getThirdPartyTypeshedPath ( execEnv , importFailureInfo ) ; if ( ! this . _cachedTypeshedThirdPartyPackagePaths ) { this . _buildTypeshedThirdPartyPackageMap ( typeshedPath ) ; } return this . _cachedTypeshedThirdPartyPackageRoots ! ; } private _getTypeshedRoot ( execEnv , importFailureInfo ) { if ( this . _cachedTypeshedRoot !== undefined ) { return this . _cachedTypeshedRoot ; } let typeshedPath = '' ; if ( this . _configOptions . typeshedPath ) { const possibleTypeshedPath = this . _configOptions . typeshedPath ; if ( this . dirExistsCached ( possibleTypeshedPath ) ) { typeshedPath = possibleTypeshedPath ; } } else { const pythonSearchPaths = this . getPythonSearchPaths ( importFailureInfo ) ; for ( const searchPath of pythonSearchPaths ) { const possibleTypeshedPath = combinePaths ( searchPath , 'typeshed' ) ; if ( this . dirExistsCached ( possibleTypeshedPath ) ) { typeshedPath = possibleTypeshedPath ; break ; } } } if ( ! typeshedPath ) { typeshedPath = PythonPathUtils . getTypeShedFallbackPath ( this . fileSystem ) || '' ; } this . _cachedTypeshedRoot = typeshedPath ; return typeshedPath ; } private _getTypeshedSubdirectory ( isStdLib , execEnv , importFailureInfo ) { if ( isStdLib ) { if ( this . _cachedTypeshedStdLibPath !== undefined ) { return this . _cachedTypeshedStdLibPath ; } } else { if ( this . _cachedTypeshedThirdPartyPath !== undefined ) { return this . _cachedTypeshedThirdPartyPath ; } } let typeshedPath = this . _getTypeshedRoot ( execEnv , importFailureInfo ) ; typeshedPath = PythonPathUtils . getTypeshedSubdirectory ( typeshedPath , isStdLib ) ; if ( ! this . dirExistsCached ( typeshedPath ) ) { return undefined ; } if ( isStdLib ) { this . _cachedTypeshedStdLibPath = typeshedPath ; } else { this . _cachedTypeshedThirdPartyPath = typeshedPath ; } return typeshedPath ; } private _resolveRelativeImport ( sourceFilePath , execEnv , moduleDescriptor , importName , importFailureInfo ) { importFailureInfo . push ( 'Attempting to resolve relative import' ) ; const directory = getDirectoryLeadingDotsPointsTo ( getDirectoryPath ( sourceFilePath ) , moduleDescriptor . leadingDots ) ; if ( ! directory ) { importFailureInfo . push ( `Invalid relative path '${ importName }'` ) ; return undefined ; } const absImport = this . resolveAbsoluteImport ( directory , execEnv , moduleDescriptor , importName , importFailureInfo , false , true ) ; return this . filterImplicitImports ( absImport , moduleDescriptor . importedSymbols ) ; } private _getCompletionSuggestionsRelative ( sourceFilePath , execEnv , moduleDescriptor , suggestions ) { const directory = getDirectoryLeadingDotsPointsTo ( getDirectoryPath ( sourceFilePath ) , moduleDescriptor . leadingDots ) ; if ( ! directory ) { return ; } this . _getCompletionSuggestionsAbsolute ( sourceFilePath , execEnv , directory , moduleDescriptor , suggestions ) ; } private _getFilesInDirectory ( dirPath ) { const entriesInDir = this . readdirEntriesCached ( dirPath ) ; const filesInDir = entriesInDir . filter ( ( f ) => f . isFile ( ) ) . map ( ( f ) => f . name ) ; entriesInDir . forEach ( ( f ) => { const linkPath = combinePaths ( dirPath , f . name ) ; if ( f . isSymbolicLink ( ) && tryStat ( this . fileSystem , linkPath ) ?. isFile ( ) ) { filesInDir . push ( f . name ) ; } } ) ; return filesInDir ; } private _getCompletionSuggestionsAbsolute ( sourceFilePath , execEnv , rootPath , moduleDescriptor , suggestions , strictOnly = true ) { let dirPath = rootPath ; const nameParts = moduleDescriptor . nameParts . map ( ( name ) => name ) ; if ( moduleDescriptor . hasTrailingDot ) { nameParts . push ( '' ) ; } const leadingDots = moduleDescriptor . leadingDots ; const parentNameParts = nameParts . slice ( 0 , - 1 ) ; if ( nameParts . length === 0 ) { this . _addFilteredSuggestionsAbsolute ( sourceFilePath , execEnv , dirPath , '' , suggestions , leadingDots , parentNameParts , strictOnly ) ; } else { for ( let i = 0 ; i < nameParts . length ; i ++ ) { if ( i === nameParts . length - 1 ) { this . _addFilteredSuggestionsAbsolute ( sourceFilePath , execEnv , dirPath , nameParts [ i ] , suggestions , leadingDots , parentNameParts , strictOnly ) ; } dirPath = combinePaths ( dirPath , nameParts [ i ] ) ; if ( ! this . dirExistsCached ( dirPath ) ) { break ; } } } } private _addFilteredSuggestionsAbsolute ( sourceFilePath , execEnv , currentPath , filter , suggestions , leadingDots , parentNameParts , strictOnly ) { const entries = getFileSystemEntriesFromDirEntries ( this . readdirEntriesCached ( currentPath ) , this . fileSystem , currentPath ) ; entries . files . forEach ( ( file ) => { const fileExtension = getFileExtension ( file , false ) . toLowerCase ( ) ; const fileWithoutExtension = stripFileExtension ( file , true ) ; if ( supportedFileExtensions . some ( ( ext ) => ext === fileExtension ) ) { if ( fileWithoutExtension === '__init__' ) { return ; } if ( filter && ! StringUtils . isPatternInSymbol ( filter , fileWithoutExtension ) ) { return ; } if ( ! this . _isUniqueValidSuggestion ( fileWithoutExtension , suggestions ) || ! this . _isResolvableSuggestion ( fileWithoutExtension , leadingDots , parentNameParts , sourceFilePath , execEnv , strictOnly ) ) { return ; } suggestions . add ( fileWithoutExtension ) ; } } ) ; entries . directories . forEach ( ( dir ) => { if ( filter && ! dir . startsWith ( filter ) ) { return ; } if ( ! this . _isUniqueValidSuggestion ( dir , suggestions ) || ! this . _isResolvableSuggestion ( dir , leadingDots , parentNameParts , sourceFilePath , execEnv , strictOnly ) ) { return ; } suggestions . add ( dir ) ; } ) ; } private _isResolvableSuggestion ( name , leadingDots , parentNameParts , sourceFilePath , execEnv , strictOnly ) { const moduleDescriptor = { leadingDots : leadingDots , nameParts : [ ... parentNameParts , name ] , importedSymbols : [ ] , } ; if ( strictOnly ) { const importName = this . formatImportName ( moduleDescriptor ) ; const importFailureInfo = [ ] ; return this . _resolveImportStrict ( importName , sourceFilePath , execEnv , moduleDescriptor , importFailureInfo ) . isImportFound ; } return this . _resolveImport ( sourceFilePath , execEnv , moduleDescriptor ) . isImportFound ; } private _isUniqueValidSuggestion ( suggestionToAdd , suggestions ) { if ( suggestions . has ( suggestionToAdd ) ) { return false ; } if ( /[.-]/ . test ( suggestionToAdd ) ) { return false ; } if ( isDunderName ( suggestionToAdd ) && suggestionToAdd !== '__future__' ) { return false ; } return true ; } protected filterImplicitImports ( importResult , importedSymbols ) { if ( importedSymbols === undefined ) { const newImportResult = Object . assign ( { } , importResult ) ; newImportResult . filteredImplicitImports = [ ] ; return newImportResult ; } if ( importedSymbols . length === 0 ) { return importResult ; } if ( importResult . implicitImports . length === 0 ) { return importResult ; } const filteredImplicitImports = importResult . implicitImports . filter ( ( implicitImport ) => { return importedSymbols . some ( ( sym ) => sym === implicitImport . name ) ; } ) ; if ( filteredImplicitImports . length === importResult . implicitImports . length ) { return importResult ; } const newImportResult = Object . assign ( { } , importResult ) ; newImportResult . filteredImplicitImports = filteredImplicitImports ; return newImportResult ; } private _findImplicitImports ( importingModuleName , dirPath , exclusions ) { const implicitImportMap = new Map < string , ImplicitImport > ( ) ; const entries = getFileSystemEntriesFromDirEntries ( this . readdirEntriesCached ( dirPath ) , this . fileSystem , dirPath ) ; for ( const fileName of entries . files ) { const fileExt = getFileExtension ( fileName ) ; let strippedFileName ; let isNativeLib = false ; if ( fileExt === '.py' || fileExt === '.pyi' ) { strippedFileName = stripFileExtension ( fileName ) ; } else if ( this . _isNativeModuleFileExtension ( fileExt ) && ! this . fileExistsCached ( `${ fileName }.py` ) && ! this . fileExistsCached ( `${ fileName }.pyi` ) ) { strippedFileName = fileName . substr ( 0 , fileName . indexOf ( '.' ) ) ; isNativeLib = true ; } else { continue ; } const filePath = combinePaths ( dirPath , fileName ) ; if ( ! exclusions . find ( ( exclusion ) => exclusion === filePath ) ) { const implicitImport = { isStubFile : fileName . endsWith ( '.pyi' ) , isNativeLib , name : strippedFileName , path : filePath , } ; const entry = implicitImportMap . get ( implicitImport . name ) ; if ( ! entry || ! entry . isStubFile ) { if ( isNativeLib ) { const nativeLibPath = combinePaths ( dirPath , fileName ) ; const nativeStubPath = this . resolveNativeImportEx ( nativeLibPath , `${ importingModuleName }.${ strippedFileName }` , [ ] ) ; if ( nativeStubPath ) { implicitImport . path = nativeStubPath ; implicitImport . isNativeLib = false ; } } implicitImportMap . set ( implicitImport . name , implicitImport ) ; } } } for ( const dirName of entries . directories ) { const pyFilePath = combinePaths ( dirPath , dirName , '__init__.py' ) ; const pyiFilePath = pyFilePath + 'i' ; let isStubFile = false ; let path = '' ; if ( this . fileExistsCached ( pyiFilePath ) ) { isStubFile = true ; path = pyiFilePath ; } else if ( this . fileExistsCached ( pyFilePath ) ) { path = pyFilePath ; } if ( path ) { if ( ! exclusions . find ( ( exclusion ) => exclusion === path ) ) { const implicitImport = { isStubFile , isNativeLib : false , name : dirName , path , } ; implicitImportMap . set ( implicitImport . name , implicitImport ) ; } } } return [ ... implicitImportMap . values ( ) ] ; } protected formatImportName ( moduleDescriptor ) { return '.' . repeat ( moduleDescriptor . leadingDots ) + moduleDescriptor . nameParts . join ( '.' ) ; } private _resolveNativeModuleStub ( nativeLibPath , execEnv , importName , moduleDescriptor , importFailureInfo , resolvedPaths ) { let moduleFullName = importName ; if ( moduleDescriptor . leadingDots > 0 ) { const info = this . getModuleNameForImport ( nativeLibPath , execEnv ) ; moduleFullName = info . moduleName . length > 0 ? info . moduleName : moduleFullName ; } const compiledStubPath = this . resolveNativeImportEx ( nativeLibPath , moduleFullName , importFailureInfo ) ; if ( compiledStubPath ) { importFailureInfo . push ( `Resolved native import ${ importName } with stub '${ compiledStubPath }'` ) ; resolvedPaths . push ( compiledStubPath ) ; return false ; } importFailureInfo . push ( `Resolved import with file '${ nativeLibPath }'` ) ; resolvedPaths . push ( nativeLibPath ) ; return true ; } private _isNativeModuleFileName ( moduleName , fileName ) { const fileExtension = getFileExtension ( fileName , false ) . toLowerCase ( ) ; const withoutExtension = stripFileExtension ( fileName , true ) ; return ( this . _isNativeModuleFileExtension ( fileExtension ) && equateStringsCaseInsensitive ( moduleName , withoutExtension ) ) ; } private _isNativeModuleFileExtension ( fileExtension ) { return supportedNativeLibExtensions . some ( ( ext ) => ext === fileExtension ) ; } private _tryWalkUp ( current ) { if ( isDiskPathRoot ( current ) ) { return [ false , '' ] ; } return [ true , ensureTrailingDirectorySeparator ( normalizePathCase ( this . fileSystem , normalizePath ( combinePaths ( current , '..' ) ) ) ) , ] ; } private _shouldWalkUp ( current , root , execEnv ) { return current . length > root . length || ( current === root && ! execEnv . root ) ; } protected getParentImportResolutionRoot ( sourceFilePath , executionRoot ) { if ( executionRoot ) { return ensureTrailingDirectorySeparator ( normalizePathCase ( this . fileSystem , normalizePath ( executionRoot ) ) ) ; } return ensureTrailingDirectorySeparator ( getDirectoryPath ( sourceFilePath ) ) ; } } export type ImportResolverFactory = ( fs : FileSystem , options : ConfigOptions , host : Host ) => ImportResolver ;