Skip to content
This repository was archived by the owner on Nov 4, 2024. It is now read-only.

Commit b4c5018

Browse files
Mikhail ArkhipovAlexanderSher
Mikhail Arkhipov
authored andcommitted
Cache function eval result per argument set (microsoft#988)
* Fix microsoft#668 (partial) * Tests * Revert "Tests" This reverts commit 7ffc9db. * Exp * Limit concurrency * Concurrency limit * Drop cache after analysis * Fix regression * Fix test * Cache argument eval * Fix hash calculation * Fix exception when there is a space before triple quote * Fix exception
1 parent fa56776 commit b4c5018

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ public IMember GetValueFromFunctionType(IPythonFunctionType fn, IPythonInstance
196196
// make sense to evaluate stubs since they already should be annotated.
197197
if (fn.DeclaringModule is IDocument doc && fd?.Ast == doc.GetAnyAst()) {
198198
// Stubs are coming from another module.
199-
return TryEvaluateWithArguments(fn.DeclaringModule, fd, args);
199+
return TryEvaluateWithArguments(fn, args);
200200
}
201201
return UnknownType;
202202
}
@@ -207,9 +207,26 @@ public IMember GetValueFromProperty(IPythonPropertyType p, IPythonInstance insta
207207
return instance.Call(p.Name, ArgumentSet.Empty);
208208
}
209209

210-
private IMember TryEvaluateWithArguments(IPythonModule module, FunctionDefinition fd, IArgumentSet args) {
210+
211+
private readonly Dictionary<int, IMember> _argEvalCache = new Dictionary<int, IMember>();
212+
213+
private IMember TryEvaluateWithArguments(IPythonFunctionType fn, IArgumentSet args) {
214+
var name = fn.DeclaringType != null ? $"{fn.DeclaringModule.Name}.{fn.Name}" : fn.Name;
215+
var argHash = args
216+
.Arguments
217+
.Select(a => a.Name.GetHashCode() ^ 397 * (a.Value?.GetHashCode() ?? 0))
218+
.Aggregate(0, (current, d) => 31 * current ^ d);
219+
var key = fn.DeclaringModule.Name.GetHashCode() ^ name.GetHashCode() ^ (397 * argHash);
220+
221+
if (_argEvalCache.TryGetValue(key, out var result)) {
222+
return result;
223+
}
224+
225+
var fd = fn.FunctionDefinition;
226+
var module = fn.DeclaringModule;
227+
211228
// Attempt to evaluate with specific arguments but prevent recursion.
212-
IMember result = UnknownType;
229+
result = UnknownType;
213230
if (fd != null && !_callEvalStack.Contains(fd)) {
214231
using (OpenScope(module, fd.Parent, out _)) {
215232
_callEvalStack.Push(fd);
@@ -222,6 +239,8 @@ private IMember TryEvaluateWithArguments(IPythonModule module, FunctionDefinitio
222239
}
223240
}
224241
}
242+
243+
_argEvalCache[key] = result;
225244
return result;
226245
}
227246

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,8 +496,11 @@ private string TryGetDocFromModuleInitFile() {
496496
}
497497

498498
if (quote != null) {
499-
// Check if it is a single-liner
500-
if (line.EndsWithOrdinal(quote) && line.IndexOf(quote, StringComparison.Ordinal) < line.LastIndexOf(quote, StringComparison.Ordinal)) {
499+
// Check if it is a single-liner, but do distinguish from """<eol>
500+
// Also, handle quadruple+ quotes.
501+
line = line.Trim();
502+
line = line.All(c => c == quote[0]) ? quote : line;
503+
if (line.EndsWithOrdinal(quote) && line.IndexOf(quote, StringComparison.Ordinal) < line.LastIndexOf(quote, StringComparison.Ordinal)) {
501504
return line.Substring(quote.Length, line.Length - 2 * quote.Length).Trim();
502505
}
503506
var sb = new StringBuilder();

0 commit comments

Comments
 (0)