public static string GetUniqueId(string moduleName, string filePath, ModuleType moduleType, IServiceContainer services, AnalysisCachingLevel cachingLevel) { if (cachingLevel == AnalysisCachingLevel.None) { return(null); } if (moduleType == ModuleType.User) { // Only for tests. return($"{moduleName}"); } var interpreter = services.GetService <IPythonInterpreter>(); var fs = services.GetService <IFileSystem>(); var moduleResolution = interpreter.ModuleResolution; var modulePathType = GetModulePathType(filePath, moduleResolution.LibraryPaths, fs); switch (modulePathType) { case PythonLibraryPathType.Site when cachingLevel < AnalysisCachingLevel.Library: return(null); case PythonLibraryPathType.StdLib when cachingLevel < AnalysisCachingLevel.System: return(null); } if (!string.IsNullOrEmpty(filePath) && modulePathType == PythonLibraryPathType.Site) { // Module can be a submodule of a versioned package. In this case we want to use // version of the enclosing package so we have to look up the chain of folders. var moduleRootName = moduleName.Split('.')[0]; var moduleFilesFolder = Path.GetDirectoryName(filePath); var installationFolder = Path.GetDirectoryName(moduleFilesFolder); var versionFolder = installationFolder; while (!string.IsNullOrEmpty(versionFolder)) { // If module is in site-packages and is versioned, then unique id = name + version + interpreter version. // Example: 'requests' and 'requests-2.21.0.dist-info'. // TODO: for egg (https://github.com/microsoft/python-language-server/issues/196), consider *.egg-info var folders = fs.GetFileSystemEntries(versionFolder, "*-*.dist-info", SearchOption.TopDirectoryOnly) .Select(Path.GetFileName) .Where(n => n.StartsWith(moduleRootName, StringComparison.OrdinalIgnoreCase)) // Module name can be capitalized differently. .ToArray(); if (folders.Length == 1) { var fileName = Path.GetFileNameWithoutExtension(folders[0]); var dash = fileName.IndexOf('-'); return($"{moduleName}({fileName.Substring(dash + 1)})"); } // Move up if nothing is found. versionFolder = Path.GetDirectoryName(versionFolder); } } var config = interpreter.Configuration; if (moduleType.IsCompiled() || string.IsNullOrEmpty(filePath) || modulePathType == PythonLibraryPathType.StdLib) { // If module is a standard library, unique id is its name + interpreter version. return($"{moduleName}({config.Version.Major}.{config.Version.Minor})"); } var parent = moduleResolution.CurrentPathResolver.GetModuleParentFromModuleName(moduleName); var hash = HashModuleFileSizes(parent); // If all else fails, hash modules file sizes. return($"{moduleName}.{(ulong)hash}"); }