Skip to content

Expose overload hint formatting as MethodSignatureFormatter and render Python-typed signatures#136

Merged
jhonabreul merged 4 commits into
QuantConnect:masterfrom
jhonabreul:feature-overload-hint-helper
Jul 10, 2026
Merged

Expose overload hint formatting as MethodSignatureFormatter and render Python-typed signatures#136
jhonabreul merged 4 commits into
QuantConnect:masterfrom
jhonabreul:feature-overload-hint-helper

Conversation

@jhonabreul

@jhonabreul jhonabreul commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

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, FormatDefaultValue move out of MethodBinder) 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 (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 optional displayName lets constructors be rendered with the type name instead of the special .ctor token (useful for consumer messages; the binder keeps .ctor).

2. Render the hinted signatures as Python signatures — parameters as name: type annotations (*name: type for params arrays), with Python types instead of C# type names, following the conversions the runtime actually performs on arguments:

  • 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 defaults rendered as Python callers access them (e.g. StringComparison.ORDINAL), bool defaults as True/False

Additionally, overloads taking PyObject parameters are skipped from the hints: they accept any Python object and render as Any, carrying no type information — they are typically the very overloads that just rejected the value. If every candidate takes a PyObject, they are shown anyway rather than producing no hint at all.

Before / after for a failed bind:

range_consolidator(Int32 range, Func[IBaseData, Decimal] selector = None)
range_consolidator(range: int, selector: Callable[[IBaseData], float] = None)

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 new TestMethodSignatureFormatter suite 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.

  • Make sure to include one or more tests for your change (TestMethodSignatureFormatter + updated TestFloatToIntConversion / TestCallbacks assertions)
  • If an enhancement PR, please create docs and at best an example
  • Ensure you have signed the .NET Foundation CLA
  • Add yourself to AUTHORS
  • Updated the CHANGELOG

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.
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)".
@jhonabreul jhonabreul changed the title Extract overload hint formatting into public MethodSignatureFormatter Expose overload hint formatting as MethodSignatureFormatter and render Python-typed signatures Jul 10, 2026
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.
@jhonabreul jhonabreul merged commit 2b8772c into QuantConnect:master Jul 10, 2026
7 checks passed
@jhonabreul jhonabreul deleted the feature-overload-hint-helper branch July 10, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants