Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Commit ae20588

Browse files
authored
Specialize getattr() (#1146)
* specialize getattr * fix args length check * test non-string argument for getattr name
1 parent 6c0d81e commit ae20588

3 files changed

Lines changed: 49 additions & 0 deletions

File tree

src/Analysis/Ast/Impl/Modules/BuiltinsPythonModule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ private void SpecializeFunctions() {
147147
Analysis.SpecializeFunction("cmp", Interpreter.GetBuiltinType(BuiltinTypeId.Int));
148148
Analysis.SpecializeFunction("dir", BuiltinsSpecializations.ListOfStrings);
149149
Analysis.SpecializeFunction("eval", Interpreter.GetBuiltinType(BuiltinTypeId.Object));
150+
Analysis.SpecializeFunction("getattr", BuiltinsSpecializations.GetAttr);
150151
Analysis.SpecializeFunction("globals", BuiltinsSpecializations.DictStringToObject);
151152
Analysis.SpecializeFunction(@"isinstance", _boolType);
152153
Analysis.SpecializeFunction(@"issubclass", _boolType);

src/Analysis/Ast/Impl/Specializations/BuiltinsSpecializations.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,5 +116,30 @@ public static IMember Open(IPythonModule declaringModule, IPythonFunctionOverloa
116116
var returnType = io?.GetMember(returnTypeName)?.GetPythonType();
117117
return returnType != null ? new PythonInstance(returnType) : null;
118118
}
119+
120+
public static IMember GetAttr(IPythonModule module, IPythonFunctionOverload overload, IArgumentSet argSet) {
121+
// TODO: Try __getattr__ first; this may not be as reliable in practice
122+
// given we could be assuming that __getattr__ always returns the same type,
123+
// which is incorrect more often than not.
124+
125+
var args = argSet.Values<IMember>();
126+
if (args.Count < 2) {
127+
return null;
128+
}
129+
130+
var o = args[0];
131+
var name = (args[1] as IPythonConstant)?.GetString();
132+
133+
IMember def = null;
134+
if (args.Count >= 3) {
135+
def = args[2];
136+
}
137+
138+
if (name == null) {
139+
return def;
140+
}
141+
142+
return o?.GetPythonType().GetMember(name) ?? def;
143+
}
119144
}
120145
}

src/Analysis/Ast/Test/ClassesTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,5 +572,28 @@ class C(B):
572572
analysis.Should().HaveClass("B").Which.Should().HaveDocumentation("class B doc");
573573
analysis.Should().HaveClass("C").Which.Should().HaveDocumentation("class C doc");
574574
}
575+
576+
[TestMethod, Priority(0)]
577+
public async Task GetAttr() {
578+
const string code = @"
579+
class A:
580+
def __init__(self):
581+
self.x = 123
582+
583+
a = A()
584+
b = getattr(a, 'x')
585+
c = getattr(a, 'y', 3.141)
586+
d = getattr(a)
587+
e = getattr()
588+
f = getattr(a, 3.141)
589+
";
590+
var analysis = await GetAnalysisAsync(code);
591+
analysis.Should().HaveVariable("a").OfType("A")
592+
.And.HaveVariable("b").OfType(BuiltinTypeId.Int)
593+
.And.HaveVariable("c").OfType(BuiltinTypeId.Float)
594+
.And.HaveVariable("d").OfType(BuiltinTypeId.Unknown)
595+
.And.HaveVariable("e").OfType(BuiltinTypeId.Unknown)
596+
.And.HaveVariable("f").OfType(BuiltinTypeId.Unknown);
597+
}
575598
}
576599
}

0 commit comments

Comments
 (0)