public ModuleDatabase(IServiceManager sm, string cacheFolder = null)
        {
            _services            = sm;
            _log                 = _services.GetService <ILogger>();
            _fs                  = _services.GetService <IFileSystem>();
            _defaultCachingLevel = AnalysisCachingLevel.Library;
            var cfs = _services.GetService <ICacheFolderService>();

            CacheFolder  = cacheFolder ?? Path.Combine(cfs.CacheFolder, $"{CacheFolderBaseName}{DatabaseFormatVersion}");
            _cacheWriter = new CacheWriter(_services.GetService <IPythonAnalyzer>(), _fs, _log, CacheFolder);
            sm.AddService(this);
        }
Пример #2
0
        public static ModuleModel FromAnalysis(IDocumentAnalysis analysis, IServiceContainer services, AnalysisCachingLevel options)
        {
            var uniqueId = analysis.Document.GetUniqueId(services, options);

            if (uniqueId == null)
            {
                // Caching level setting does not permit this module to be persisted.
                return(null);
            }

            var variables   = new Dictionary <string, VariableModel>();
            var functions   = new Dictionary <string, FunctionModel>();
            var classes     = new Dictionary <string, ClassModel>();
            var typeVars    = new Dictionary <string, TypeVarModel>();
            var namedTuples = new Dictionary <string, NamedTupleModel>();
            // Go directly through variables which names are listed in GetMemberNames
            // as well as variables that are declarations.
            var exportedNames = new HashSet <string>(analysis.Document.GetMemberNames());

            foreach (var v in analysis.GlobalScope.Variables
                     .Where(v => exportedNames.Contains(v.Name) ||
                            v.Source == VariableSource.Declaration ||
                            v.Source == VariableSource.Builtin ||
                            v.Source == VariableSource.Generic))
            {
                if (v.Value is IGenericTypeParameter && !typeVars.ContainsKey(v.Name))
                {
                    typeVars[v.Name] = TypeVarModel.FromGeneric(v);
                    continue;
                }

                switch (v.Value)
                {
                case ITypingNamedTupleType nt:
                    namedTuples[nt.Name] = new NamedTupleModel(nt);
                    continue;

                case IPythonFunctionType ft when ft.IsLambda():
                    // No need to persist lambdas.
                    continue;

                case IPythonFunctionType ft when v.Name != ft.Name:
                    // Variable assigned to type info of the function like
                    //    def func(): ...
                    //    x = type(func)
                    break;

                case IPythonFunctionType ft:
                    var fm = GetFunctionModel(analysis, v, ft);
                    if (fm != null && !functions.ContainsKey(ft.Name))
                    {
                        functions[ft.Name] = fm;
                        continue;
                    }
                    break;

                case IPythonClassType cls when v.Name != cls.Name:
                    // Variable assigned to type info of the class.
                    break;

                case IPythonClassType cls
                    when cls.DeclaringModule.Equals(analysis.Document) || cls.DeclaringModule.Equals(analysis.Document.Stub):
                    if (!classes.ContainsKey(cls.Name))
                    {
                        classes[cls.Name] = new ClassModel(cls);
                        continue;
                    }

                    break;
                }

                // Do not re-declare classes and functions as variables in the model.
                if (!variables.ContainsKey(v.Name))
                {
                    variables[v.Name] = VariableModel.FromVariable(v);
                }
            }

            // Take dependencies from imports. If module has stub we should also take
            // dependencies from there since persistent state is based on types that
            // are combination of stub and the module. Sometimes stub may import more
            // and we must make sure dependencies are restored before the module.
            var primaryDependencyWalker = new DependencyWalker(analysis.Ast);
            var stubDependencyWalker    = analysis.Document.Stub != null ? new DependencyWalker(analysis.Document.Stub.Analysis.Ast) : null;
            var stubImports             = stubDependencyWalker?.Imports ?? Enumerable.Empty <ImportModel>();
            var stubFromImports         = stubDependencyWalker?.FromImports ?? Enumerable.Empty <FromImportModel>();

            return(new ModuleModel {
                Id = uniqueId.GetStableHash(),
                UniqueId = uniqueId,
                Name = analysis.Document.Name,
                QualifiedName = analysis.Document.QualifiedName,
                Documentation = analysis.Document.Documentation,
                Functions = functions.Values.ToArray(),
                Variables = variables.Values.ToArray(),
                Classes = classes.Values.ToArray(),
                TypeVars = typeVars.Values.ToArray(),
                NamedTuples = namedTuples.Values.ToArray(),
                NewLines = analysis.Ast.NewLineLocations.Select(l => new NewLineModel {
                    EndIndex = l.EndIndex,
                    Kind = l.Kind
                }).ToArray(),
                FileSize = analysis.Ast.EndIndex,
                Imports = primaryDependencyWalker.Imports.ToArray(),
                FromImports = primaryDependencyWalker.FromImports.ToArray(),
                StubImports = stubImports.ToArray(),
                StubFromImports = stubFromImports.ToArray()
            });
        }
Пример #3
0
        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}");
        }
Пример #4
0
 public static string GetUniqueId(this IPythonModule module, IServiceContainer services, AnalysisCachingLevel cachingLevel)
 => GetUniqueId(module.Name, module.FilePath, module.ModuleType, services, cachingLevel);
 public static string GetUniqueId(this IPythonModule module, IServiceContainer services, AnalysisCachingLevel cachingLevel = AnalysisCachingLevel.Library)
 {
     // If module is a standalone stub, permit it. Otherwise redirect to the main module
     // since during stub merge types from stub normally become part of the primary module.
     if (module.ModuleType == ModuleType.Stub && module.PrimaryModule != null)
     {
         module = module.PrimaryModule;
     }
     return(GetUniqueId(module.Name, module.FilePath, module.ModuleType, services, cachingLevel));
 }
        /// <summary>
        /// Constructs module persistent model from analysis.
        /// </summary>
        public static ModuleModel FromAnalysis(IDocumentAnalysis analysis, IServiceContainer services, AnalysisCachingLevel options)
        {
            var uniqueId = analysis.Document.GetUniqueId(services, options);

            if (uniqueId == null)
            {
                // Caching level setting does not permit this module to be persisted.
                return(null);
            }

            var variables   = new Dictionary <string, VariableModel>();
            var functions   = new Dictionary <string, FunctionModel>();
            var classes     = new Dictionary <string, ClassModel>();
            var typeVars    = new Dictionary <string, TypeVarModel>();
            var namedTuples = new Dictionary <string, NamedTupleModel>();

            //var subModules = new Dictionary<string, SubmoduleModel>();

            foreach (var v in analysis.Document.GetMemberNames()
                     .Select(x => analysis.GlobalScope.Variables[x]).ExcludeDefault())
            {
                if (v.Value is IGenericTypeParameter && !typeVars.ContainsKey(v.Name))
                {
                    typeVars[v.Name] = TypeVarModel.FromGeneric(v, services);
                    continue;
                }

                switch (v.Value)
                {
                case ITypingNamedTupleType nt:
                    namedTuples[v.Name] = new NamedTupleModel(nt, services);
                    continue;

                case IPythonFunctionType ft when ft.IsLambda():
                    // No need to persist lambdas.
                    continue;

                case IPythonFunctionType ft when v.Name != ft.Name:
                    // Variable assigned to type info of the function like
                    //    def func(): ...
                    //    x = type(func)
                    break;

                case IPythonFunctionType ft:
                    var fm = GetFunctionModel(analysis, v, ft, services);
                    if (fm != null && !functions.ContainsKey(ft.Name))
                    {
                        functions[ft.Name] = fm;
                        continue;
                    }
                    break;

                case IPythonClassType cls when v.Name != cls.Name:
                    // Variable assigned to type info of the class.
                    break;

                case IPythonClassType cls
                    when cls.DeclaringModule.Equals(analysis.Document) || cls.DeclaringModule.Equals(analysis.Document.Stub):
                    if (!classes.ContainsKey(cls.Name))
                    {
                        classes[cls.Name] = new ClassModel(cls, services);
                        continue;
                    }

                    break;
                }

                // Do not re-declare classes and functions as variables in the model.
                if (!variables.ContainsKey(v.Name))
                {
                    variables[v.Name] = VariableModel.FromVariable(v, services);
                }
            }

            return(new ModuleModel {
                Id = uniqueId.GetStableHash(),
                UniqueId = uniqueId,
                Name = analysis.Document.Name,
                QualifiedName = analysis.Document.QualifiedName,
                FilePath = analysis.Document.FilePath,
                Documentation = analysis.Document.Documentation,
                Functions = functions.Values.ToArray(),
                Variables = variables.Values.ToArray(),
                Classes = classes.Values.ToArray(),
                TypeVars = typeVars.Values.ToArray(),
                NamedTuples = namedTuples.Values.ToArray(),
                //SubModules = subModules.Values.ToArray(),
                NewLines = analysis.Ast.NewLineLocations.Select(l => new NewLineModel {
                    EndIndex = l.EndIndex,
                    Kind = l.Kind
                }).ToArray(),
                FileSize = analysis.Ast.EndIndex
            });
        }