Expose overload hint formatting as MethodSignatureFormatter and render Python-typed signatures#136
Merged
jhonabreul merged 4 commits intoJul 10, 2026
Conversation
Move the overload-signature hint logic added in QuantConnect#128 (AppendOverloads, FormatSignature, FormatType, SnakeCaseName, FormatDefaultValue) out of MethodBinder into a new public MethodSignatureFormatter class so it can be reused outside the binder. Downstream consumers (e.g. Lean) can now append the same "The following overloads are available:" hint to their own user-facing error messages when they reject a PyObject argument themselves. - FormatOverloads(methods, maxShown = 10, displayName = null) returns the hint text ("The expected signature is:" / "The following overloads are available:" plus one signature per line), or an empty string. Still best-effort: it never throws. - FormatSignature(method, displayName = null) is public; the optional displayName lets constructors render as the type name instead of the special .ctor token. - MethodBinder behavior is unchanged: the "No method matches given arguments" message is byte-identical.
Open
11 tasks
Martin-Molinero
approved these changes
Jul 10, 2026
The hinted signatures showed C# type names (Int32, TimeSpan, Func[...]), which a Python caller has to mentally translate. Render them with the Python types the runtime actually accepts for each parameter instead: - str/int/float/bool for strings, chars and numeric primitives - datetime / timedelta for DateTime / TimeSpan - Optional[T] for Nullable<T> - Callable[[args], ret] for delegates (None return for actions) - List[T] / Dict[K, V] for arrays, list and dictionary shapes - Any for object and PyObject (List[Any]/Dict[Any, Any] for PyList/PyDict) - CLR-only types (enums, classes) keep their Python-visible name Enum default values are rendered the way Python callers access them (e.g. StringComparison.ORDINAL) and bool defaults as True/False. Example: "range_consolidator(Int32 range, Func[IBaseData, Decimal] selector = None)" now renders as "range_consolidator(int range, Callable[[IBaseData], float] selector = None)".
Python signatures annotate the name, not prefix the type: an argument rendered as "int arg_name" is actually "arg_name: int". Render the hinted signatures accordingly, e.g.: range_consolidator(range: int, selector: Callable[[IBaseData], float] = None) params arrays are rendered in Python variadic form, annotated with the element type: *values: int.
PyObject parameters accept any Python object and render as Any, so hinting them carries no type information: they are typically the very overloads that just rejected the value. Skip them in FormatOverloads so consumers do not have to filter them out themselves. If every candidate takes a PyObject, they are shown anyway rather than producing no hint at all.
Martin-Molinero
approved these changes
Jul 10, 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.
Two related improvements to the overload hints added in #128:
1. Extract the hint logic into a public
MethodSignatureFormatter(AppendOverloads,FormatSignature,FormatType,SnakeCaseName,FormatDefaultValuemove out ofMethodBinder) so it can be reused outside the binder. Downstream consumers (e.g. Lean) can now append the same "The following overloads are available:" hint to their own user-facing error messages when they reject aPyObjectargument themselves (see the argument validation improvements in QuantConnect/Lean#9607, where e.g.QuoteBarConsolidator(Resolution.DAILY)now lists the consolidator's constructor overloads).Public API:
MethodSignatureFormatter.FormatOverloads(IEnumerable<MethodBase> methods, int maxShown = 10, string displayName = null)— returns the hint text (The expected signature is:/The following overloads are available:followed by one signature per line, distinct and capped), or an empty string. Still best-effort: it never throws.MethodSignatureFormatter.FormatSignature(MethodBase method, string displayName = null)— the snake_case signature renderer; the optionaldisplayNamelets constructors be rendered with the type name instead of the special.ctortoken (useful for consumer messages; the binder keeps.ctor).2. Render the hinted signatures as Python signatures — parameters as
name: typeannotations (*name: typeforparamsarrays), with Python types instead of C# type names, following the conversions the runtime actually performs on arguments:str/int/float/boolfor strings, chars and numeric primitivesdatetime/timedeltaforDateTime/TimeSpanOptional[T]forNullable<T>Callable[[args], ret]for delegates (Nonereturn for actions)List[T]/Dict[K, V]for arrays, list and dictionary shapesAnyforobjectandPyObject(List[Any]/Dict[Any, Any]forPyList/PyDict)StringComparison.ORDINAL), bool defaults asTrue/FalseAdditionally, overloads taking
PyObjectparameters are skipped from the hints: they accept any Python object and render asAny, carrying no type information — they are typically the very overloads that just rejected the value. If every candidate takes aPyObject, they are shown anyway rather than producing no hint at all.Before / after for a failed bind:
Does this close any currently open issues?
No.
Any other comments?
The binder's "No method matches given arguments" message structure is unchanged; only the type names inside the hinted signatures changed. Existing message tests (
TestFloatToIntConversion,TestCallbacks) updated where they asserted C# type names, and a newTestMethodSignatureFormattersuite covers the type mapping (primitives, datetime/timedelta, Optional, Callable, List/Dict, Any, CLR-only names, constructor display names).Checklist
Check all those that are applicable and complete.
TestMethodSignatureFormatter+ updatedTestFloatToIntConversion/TestCallbacksassertions)AUTHORSCHANGELOG