private void MergeMembers(IVariable v, IPythonType sourceType, IPythonType stubType, CancellationToken cancellationToken) { // Transfer documentation first so we get class documentation // that comes from the class definition win over one that may // come from __init__ during the member merge below. TransferDocumentationAndLocation(sourceType.GetPythonType(), stubType); // Replace the class entirely since stub members may use generic types // and the class definition is important. We transfer missing members // from the original class to the stub. // // In case module is compiled, it is already a stub and has no locations // for code navigation.. In this case we replace the entire variable by one // from the stub rather than just the value since stub variable has location // and its own root definition/reference chain. if (sourceType.DeclaringModule.ModuleType == ModuleType.Compiled || sourceType.DeclaringModule.ModuleType == ModuleType.CompiledBuiltin) { _eval.ReplaceVariable(v); } else { _eval.DeclareVariable(v.Name, v.Value, v.Source); } // First pass: go through source class members and pick those // that are not present in the stub class. foreach (var name in sourceType.GetMemberNames().ToArray()) { cancellationToken.ThrowIfCancellationRequested(); var sourceMember = sourceType.GetMember(name); if (sourceMember.IsUnknown()) { continue; // Do not add unknowns to the stub. } var sourceMemberType = sourceMember?.GetPythonType(); if (sourceMemberType is IPythonClassMember cm && !cm.DeclaringModule.Equals(sourceType.DeclaringModule)) { continue; // Only take members from this class and not from bases. } if (!IsFromThisModuleOrSubmodules(sourceMemberType)) { continue; // Member does not come from module or its submodules. } var stubMember = stubType.GetMember(name); var stubMemberType = stubMember.GetPythonType(); // Get documentation from the current type, if any, since stubs // typically do not contain documentation while scraped code does. TransferDocumentationAndLocation(sourceMemberType, stubMemberType); // If stub says 'Any' but we have better type, use member from the original class. if (stubMember != null && !(stubType.DeclaringModule is TypingModule && stubType.Name == "Any")) { continue; // Stub already have the member, don't replace. } (stubType as PythonType)?.AddMember(name, sourceMember, overwrite: true); } // Second pass: go through stub class members and if they don't have documentation // or location, check if source class has same member and fetch it from there. // The reason is that in the stub sometimes members are specified all in one // class while in source they may come from bases. Example: datetime. foreach (var name in stubType.GetMemberNames().ToArray()) { cancellationToken.ThrowIfCancellationRequested(); var stubMember = stubType.GetMember(name); if (stubMember.IsUnknown()) { continue; } var stubMemberType = stubMember.GetPythonType(); if (stubMemberType is IPythonClassMember cm && cm.DeclaringType != stubType) { continue; // Only take members from this class and not from bases. } var sourceMember = sourceType.GetMember(name); if (sourceMember.IsUnknown()) { continue; } var sourceMemberType = sourceMember.GetPythonType(); if (sourceMemberType.Location.IsValid && !stubMemberType.Location.IsValid) { TransferDocumentationAndLocation(sourceMemberType, stubMemberType); } } }