Cache AttributeError member-name suggestions#130
Open
jhonabreul wants to merge 1 commit into
Open
Conversation
Building the "Did you mean ...?" hint for a missing attribute reflected over the managed type's full member set (GetMembers with FlattenHierarchy), snake_cased every member, and ran a Levenshtein scan -- on every miss, with no caching. getattr(obj, name, default) and hasattr trigger it too, since the work happens on the miss path before CPython suppresses the error. A workload that probes the same missing names repeatedly (e.g. a per-bar getattr(self, "_optional", None) on a .NET-derived object) therefore paid the full O(members) reflection + ranking cost on every access. Add two caches in ClassBase: - _candidateNameCache (Type -> string[]): the reflected, deduplicated snake_case member names, computed once per type. - _suggestionCache ((Type, name) -> string[]): the ranked suggestion list, memoized so repeated misses of the same name are a dictionary lookup. Suggestions and error messages are unchanged; only the repeated computation is removed. On a real Lean multi-symbol minute backtest the suggestion path dominated ~93% of OnData CPU; with this change the backtest goes from not finishing (aborted, >15x slower) to ~93s, on par with the last build without the suggestion feature (~90s), with identical results. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Martin-Molinero
approved these changes
Jul 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this implement/fix? Explain your changes.
Caches the AttributeError "Did you mean ...?" member-name suggestions so they are not recomputed on every attribute miss.
Building the hint (added in #124 / #129) reflects over the managed type's entire member set (
GetMemberswithFlattenHierarchy), snake_cases every member, and runs a Levenshtein scan. This ran on every miss with no caching — andgetattr(obj, name, default)/hasattrtrigger it too, because the work happens on the miss path before CPython suppresses the error. A workload that probes the same missing names repeatedly — e.g. a per-bargetattr(self, "_optional", None)on a .NET-derived object — pays the fullO(members)reflection + ranking cost on every call.This adds two caches in
ClassBase:_candidateNameCache(Type -> string[]): the deduplicated snake_case member names, computed once per type._suggestionCache((Type, name) -> string[]): the ranked suggestion list, memoized per(type, missing-name).The reflection/ranking moved into
ComputeSimilarMemberNames/GetCandidateMemberNames;GetSimilarMemberNamesnow returns from the memo. Suggestions and error-message text are unchanged — only the repeated computation is removed.Measured on a real Lean multi-symbol minute backtest (a Python
QCAlgorithmsubclass doing many per-bar optional-attribute probes), profiled withdotnet-trace: the suggestion path (AttributeErrorHint.GetAttrHook -> GetSimilarMemberNames -> GetMembers / LevenshteinDistance / ToSnakeCase) accounted for ~93% of OnData CPU. With this change the same backtest goes from not finishing (aborted, >15x slower) to ~93s, on par with the last build without the suggestion feature (~90s), with identical results.Does this close any currently open issues?
No pythonnet issue is open. This fixes the backtest performance regression introduced with the AttributeError suggestion feature (#124 / #129).
Any other comments?
Behavior-preserving: the emitted error messages and suggestions are byte-for-byte identical, so there are no user-visible output changes — only the redundant per-miss work is eliminated.
Checklist
AUTHORSCHANGELOG