From a9cc2b3b4db128d5ac0c4416610d876e716ad2f9 Mon Sep 17 00:00:00 2001 From: MikhailArkhipov Date: Thu, 11 Jul 2019 14:05:20 -0700 Subject: [PATCH 1/2] Test --- src/Analysis/Ast/Test/ClassesTests.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Analysis/Ast/Test/ClassesTests.cs b/src/Analysis/Ast/Test/ClassesTests.cs index 19e6639d2..842b81440 100644 --- a/src/Analysis/Ast/Test/ClassesTests.cs +++ b/src/Analysis/Ast/Test/ClassesTests.cs @@ -586,6 +586,31 @@ def __init__(self): d = getattr(a) e = getattr() f = getattr(a, 3.141) +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("a").OfType("A") + .And.HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.Float) + .And.HaveVariable("d").OfType(BuiltinTypeId.Unknown) + .And.HaveVariable("e").OfType(BuiltinTypeId.Unknown) + .And.HaveVariable("f").OfType(BuiltinTypeId.Unknown); + } + + [TestMethod, Priority(0)] + public async Task ProxyBase() { + const string code = @" +from weakref import proxy + +class C0(): pass +class C1(C0): pass +class C2(C1): pass + +class Test(): + def __init__(self): + p = proxy(self) + + F1 = C1 + F2 = C2 "; var analysis = await GetAnalysisAsync(code); analysis.Should().HaveVariable("a").OfType("A") From 122b74dbf0006d0d3a97b62e26e622fa29606fae Mon Sep 17 00:00:00 2001 From: MikhailArkhipov Date: Thu, 11 Jul 2019 16:11:25 -0700 Subject: [PATCH 2/2] Cache specific types Do not create specific types for non-generics --- src/Analysis/Ast/Impl/Types/ArgumentSet.cs | 1 - .../Ast/Impl/Types/PythonClassType.cs | 19 ++++++++----- src/Analysis/Ast/Test/ClassesTests.cs | 28 ++++++++++++++----- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/Analysis/Ast/Impl/Types/ArgumentSet.cs b/src/Analysis/Ast/Impl/Types/ArgumentSet.cs index 180bbaf63..edefe2b83 100644 --- a/src/Analysis/Ast/Impl/Types/ArgumentSet.cs +++ b/src/Analysis/Ast/Impl/Types/ArgumentSet.cs @@ -17,7 +17,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using Microsoft.Python.Analysis.Analyzer; using Microsoft.Python.Analysis.Diagnostics; using Microsoft.Python.Analysis.Extensions; diff --git a/src/Analysis/Ast/Impl/Types/PythonClassType.cs b/src/Analysis/Ast/Impl/Types/PythonClassType.cs index 330e547dd..34282805a 100644 --- a/src/Analysis/Ast/Impl/Types/PythonClassType.cs +++ b/src/Analysis/Ast/Impl/Types/PythonClassType.cs @@ -17,8 +17,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using Microsoft.Python.Analysis.Analyzer; -using Microsoft.Python.Analysis.Diagnostics; using Microsoft.Python.Analysis.Modules; using Microsoft.Python.Analysis.Specializations.Typing; using Microsoft.Python.Analysis.Types.Collections; @@ -34,6 +32,8 @@ namespace Microsoft.Python.Analysis.Types { [DebuggerDisplay("Class {Name}")] internal class PythonClassType : PythonType, IPythonClassType, IPythonTemplateType, IEquatable { private static readonly string[] _classMethods = { "mro", "__dict__", @"__weakref__" }; + + private Dictionary _specificTypeCache; private IPythonClassType _processing; private List _bases; private IReadOnlyList _mro; @@ -353,19 +353,20 @@ public IPythonType CreateSpecificType(IArgumentSet args) { } // For still undefined parameters try matching passed types in order - for (var i = 0; i < args.Arguments.Count; i++) { + for (int i = 0, gtIndex = 0; i < args.Arguments.Count; i++) { var arg = args.Arguments[i]; if (Equals(arg.Type)) { - continue; + continue; // Typically 'self'. } if (arg.Value is IMember member) { var type = member.GetPythonType(); if (!type.IsUnknown()) { - var gtd = i < genericTypeDefinitions.Count ? genericTypeDefinitions[i] : null; + var gtd = gtIndex < genericTypeDefinitions.Count ? genericTypeDefinitions[gtIndex] : null; if (gtd != null && !specificClassTypeParameters.ContainsKey(gtd.Name)) { specificClassTypeParameters[gtd.Name] = type; } + gtIndex++; } } } @@ -377,7 +378,11 @@ public IPythonType CreateSpecificType(IArgumentSet args) { .ToArray(); var specificName = CodeFormatter.FormatSequence(Name, '[', specificTypes); - var classType = new PythonClassType(specificName, new Location(DeclaringModule)); + _specificTypeCache = _specificTypeCache ?? new Dictionary(); + if (_specificTypeCache.TryGetValue(specificName, out var classType)) { + return classType; + } + _specificTypeCache[specificName] = classType = new PythonClassType(specificName, new Location(DeclaringModule)); // Methods returning generic types need to know how to match generic // parameter name to the actual supplied type. @@ -490,7 +495,7 @@ private void SetClassMembers(PythonClassType classType, IArgumentSet args) { // Functions handle generics internally upon the call to Call. foreach (var m in members) { switch (m.Value) { - case IPythonTemplateType tt: { + case IPythonTemplateType tt when tt.IsGeneric(): { var specificType = tt.CreateSpecificType(args); classType.AddMember(m.Key, specificType, true); break; diff --git a/src/Analysis/Ast/Test/ClassesTests.cs b/src/Analysis/Ast/Test/ClassesTests.cs index 842b81440..1d817f3cb 100644 --- a/src/Analysis/Ast/Test/ClassesTests.cs +++ b/src/Analysis/Ast/Test/ClassesTests.cs @@ -13,6 +13,7 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -604,6 +605,12 @@ from weakref import proxy class C0(): pass class C1(C0): pass class C2(C1): pass +class C3(C2): pass +class C4(C3): pass +class C5(C4): pass +class C6(C5): pass +class C7(C6): pass +class C8(C7): pass class Test(): def __init__(self): @@ -611,14 +618,21 @@ def __init__(self): F1 = C1 F2 = C2 + F3 = C3 + F4 = C4 + F5 = C5 + F6 = C6 + F7 = C7 + F8 = C8 "; - var analysis = await GetAnalysisAsync(code); - analysis.Should().HaveVariable("a").OfType("A") - .And.HaveVariable("b").OfType(BuiltinTypeId.Int) - .And.HaveVariable("c").OfType(BuiltinTypeId.Float) - .And.HaveVariable("d").OfType(BuiltinTypeId.Unknown) - .And.HaveVariable("e").OfType(BuiltinTypeId.Unknown) - .And.HaveVariable("f").OfType(BuiltinTypeId.Unknown); + // Verifies that analysis of the fragment completes in reasonable time. + // see https://github.com/microsoft/python-language-server/issues/1291. + var sw = Stopwatch.StartNew(); + await GetAnalysisAsync(code); + sw.Stop(); + // Desktop: product time is typically less few seconds second. + // Test run time: typically ~ 20 sec. + sw.ElapsedMilliseconds.Should().BeLessThan(60000); } } }