| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
… the instance C# extension methods were only reachable from Python through their static class, so instance-style calls such as order_status.is_open() failed with AttributeError. Extension methods declared in non-framework assemblies are now indexed during the assembly scan and bound as members of the type they extend, including interface targets on implementing classes. The binder passes the instance as the first argument when an extension method is called through it. Real members with the same name win, and open generic targets are skipped since the binder cannot infer them from the instance.
Targets made only of framework types are shared by countless classes: extensions on object reached every CLR object, and Accord.Math alone added ~600 methods to each primitive array type. Across Lean's assemblies the index shrinks from 16487 to 653 methods. Such extensions keep their static form. Assemblies without the compiler's assembly-level [Extension] marker are skipped up front, as C# does, and binding a class only walks its interfaces when an interface target was indexed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review follow-up: pushed 86be7c6 and e2af525Where: src/runtime/ExtensionMethods.cs, src/embed_tests/TestExtensionMethods.cs, CHANGELOG.md
WhatExtension methods are now bound to an instance only when their target involves a non-framework type: the type itself, an array's element type, or a generic argument.
Extensions that are not bound still work through their static class, e.g. Extensions.convert_invariant(x). WhyOnly the declaring assembly was checked against the framework list, not the extended type. Measured over the 88 assemblies in Lean's Launcher/bin/Debug:
How
Result: the index goes from 16,487 to 653 methods. The one-time Register cost goes from about 30 ms to 24 ms, most of which is now reading Accord's methods just to discard them. I considered making the scan lazy, but it wouldn't help: Lean binds classes right away, and finding which static class extends a type requires the scan anyway. Behaviour change: the existing this List<int> test now asserts that the extension is not bound. The positive closed-generic case moved to List<ExtendedModel> and IEnumerable<ExtendedModel>. Hiding helpers with [EditorBrowsable(Never)] (e2af525)What: extension methods marked [EditorBrowsable(EditorBrowsableState.Never)], and all extension methods of a static class marked so, are no longer bound to the instance. Their static form keeps working. Advanced is not affected. Why: binding exposes engine plumbing on user objects. For example, every algorithm gets self.set_runtime_error, self.process_security_changes, self.create_option_chain, self.create_future_chain and self.get_or_add_unrequested_security from Lean's IAlgorithm extensions. EditorBrowsable(Never) is the standard .NET way to say "callable, but don't advertise it" (IntelliSense hides such members), so pythonnet honors it without knowing any Lean-specific attribute. How: IsHidden checks the attribute on the extension class and on each method that passes the target filter.
Note: the stubs generator (QuantConnect/quantconnect-stubs-generator#104) should skip the instance form for these methods too. Otherwise the stubs would accept instance calls that fail at runtime. Tests
🤖 Generated with Claude Code |
Sorry, something went wrong.
…form [EditorBrowsable(EditorBrowsableState.Never)] hides a member from IntelliSense while keeping it callable. Binding such extension methods to the instance would advertise them anyway, so those methods, and all the extension methods of a class marked so, keep their static form only. It lets a library hide plumbing helpers, like Lean's IAlgorithm extensions, from the instance with a standard attribute. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| Back | FazBrowse Home | New Git URL |
What does this implement/fix? Explain your changes.
C# extension methods were only reachable from Python through their static class, so the instance-style call every C# example uses failed:
Cause: ClassManager builds a type's Python class from its real members only, and extension methods are static methods on another type.
The change:
Limits: targets converted to Python natives (string, decimal, DateTime, double) cannot be bound, and their extensions stay available through the static class.
Does this close any currently open issues?
No.
Any other comments?
Tests, in TestExtensionMethods: