}                       // For de-serializer from JSON

        /// <summary>
        /// Constructs class model for persistence off the class in-memory type.
        /// </summary>
        public ClassModel(IPythonClassType cls, IServiceContainer services)
        {
            var methods      = new List <FunctionModel>();
            var properties   = new List <PropertyModel>();
            var fields       = new List <VariableModel>();
            var innerClasses = new List <ClassModel>();

            // Skip certain members in order to avoid infinite recursion.
            foreach (var name in cls.GetMemberNames().Except(new[] { "__base__", "__bases__", "__class__", "mro" }))
            {
                var m = cls.GetMember(name);

                // Only take members from this class, skip members from bases.
                if (m is IPythonClassMember cm && cls.QualifiedName != cm.DeclaringType?.QualifiedName)
                {
                    continue;
                }

                using (_processing.Push(m, out var reentered)) {
                    if (reentered)
                    {
                        continue;
                    }

                    switch (m)
                    {
                    case IPythonClassType ct when ct.Name == name:
                        if (!ct.DeclaringModule.Equals(cls.DeclaringModule))
                        {
                            continue;
                        }
                        innerClasses.Add(new ClassModel(ct, services));
                        break;

                    case IPythonFunctionType ft when ft.IsLambda():
                        break;

                    case IPythonFunctionType ft when ft.Name == name:
                        methods.Add(new FunctionModel(ft, services));
                        break;

                    case IPythonPropertyType prop when prop.Name == name:
                        properties.Add(new PropertyModel(prop, services));
                        break;

                    case IPythonInstance inst:
                        fields.Add(VariableModel.FromInstance(name, inst, services));
                        break;

                    case IPythonType t:
                        fields.Add(VariableModel.FromType(name, t, services));
                        break;
                    }
                }
            }

            Name = cls.TypeId == BuiltinTypeId.Ellipsis ? "ellipsis" : cls.Name;
            Id   = Name.GetStableHash();
            DeclaringModuleId = cls.DeclaringModule.GetUniqueId(services);
            QualifiedName     = cls.QualifiedName;
            IndexSpan         = cls.Location.IndexSpan.ToModel();
            Documentation     = cls.Documentation;

            var ntBases = cls.Bases.MaybeEnumerate().OfType <ITypingNamedTupleType>().ToArray();

            NamedTupleBases = ntBases.Select(b => new NamedTupleModel(b, services)).ToArray();

            Bases      = cls.Bases.MaybeEnumerate().Except(ntBases).Select(t => t.GetPersistentQualifiedName(services)).ToArray();
            Methods    = methods.ToArray();
            Properties = properties.ToArray();
            Fields     = fields.ToArray();
            Classes    = innerClasses.ToArray();

            if (cls.IsGeneric)
            {
                // Only check immediate bases, i.e. when class itself has Generic[T] base.
                var gcp = cls.Bases.OfType <IGenericClassBase>().FirstOrDefault();
                GenericBaseParameters = gcp?.TypeParameters.Select(p => p.Name).ToArray();
            }
            // If class is generic, we must save its generic base definition
            // so on restore we'll be able to re-create the class as generic.
            GenericBaseParameters = GenericBaseParameters ?? Array.Empty <string>();

            GenericParameterValues = cls.GenericParameters
                                     .Select(p => new GenericParameterValueModel {
                Name = p.Key, Type = p.Value.GetPersistentQualifiedName(services)
            })
                                     .ToArray();
        }
        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()
            });
        }
        /// <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
            });
        }