Skip to content

Accept integral floats for int params, hint overloads on bind failure (2.0.57)#128

Open
jhonabreul wants to merge 4 commits into
QuantConnect:masterfrom
jhonabreul:bug-integral-float-to-int-conversion
Open

Accept integral floats for int params, hint overloads on bind failure (2.0.57)#128
jhonabreul wants to merge 4 commits into
QuantConnect:masterfrom
jhonabreul:bug-integral-float-to-int-conversion

Conversation

@jhonabreul

@jhonabreul jhonabreul commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Two related improvements to method/constructor argument binding, plus a version bump.

1. Accept integral-valued floats for integer parameters; reject non-integral ones.

Passing a Python float where a .NET integer parameter was expected behaved inconsistently:

  • Single-overload targets silently truncated any float (e.g. 5.55) via Converter.ToManaged.
  • Overloaded targets rejected every float, including integral-valued ones (e.g. 5.0), with "No method matches given arguments", because the overload-disambiguation path did not treat floatint as a valid conversion. This broke calls like RangeConsolidator(period) in Lean when period was a float.

Both paths are now consistent: an integral-valued float (5.0) is accepted and converted for integer parameters, while a non-integral float (5.5) is rejected with a TypeError instead of being silently truncated.

  • MethodBinder: treat integral Python floats as implicit-conversion candidates for integer parameters (enums excluded).
  • Converter.ToPrimitive: reject non-integral Python floats targeting integer types so truncation never happens silently.
  • Added a shared Type.IsInteger() helper in Util and used it in both places.

2. Hint the candidate overloads in the "No method matches" TypeError.

When binding failed, the error only reported the argument types that were passed, giving no clue what the method actually expected. The message now appends the candidate overload signature(s), using the snake_case names Python callers use (method names and parameter names; constructors keep the special .ctor token). This applies to every "no match" case, not just numeric conversions.

Single candidate — a method taking a single int, showing the snake_case method and parameter name:

No method matches given arguments for compute_scaled: (<class 'float'>). The expected signature is:
  compute_scaled(Int32 scale_factor)

Multiple candidates — an overloaded method, each candidate listed in snake_case:

No method matches given arguments for compute_range: (<class 'float'>). The following overloads are available:
  compute_range(Int32 value, Func[Int32, Int32] selector = None)
  compute_range(Int32 value, PyObject selector, PyObject other = None)

The Int32 parameter is now visible, hinting that an integer was expected. For Lean's RangeConsolidator(5.5) the two constructor overloads are likewise listed with snake_case parameters (e.g. volume_selector). Signatures are rendered readably (friendly type names, by-ref/nullable unwrapped, generics as Name[Arg1, Arg2], params marked, optional parameters shown with their default). The list is de-duplicated and capped at 10 with a "... and N more" suffix. The whole hint is best-effort and wrapped in a try/catch so it can never mask the original binding failure.

3. Version bump. QuantConnect.pythonnet <Version> bumped 2.0.562.0.57 (with the matching AssemblyVersion/AssemblyFileVersion in Properties/AssemblyInfo.cs).

jhonabreul and others added 3 commits July 2, 2026 16:26
…gral

Passing a Python float where a .NET integer parameter was expected behaved
inconsistently:

- Single-overload targets silently truncated any float (e.g. 5.5 -> 5) via
  Converter.ToManaged.
- Overloaded targets rejected every float, including integral-valued ones
  (e.g. 5.0), with "No method matches given arguments" because the overload
  disambiguation path did not treat float->int as a valid conversion. This
  broke calls like RangeConsolidator(period) in Lean when period was a float.

Make both paths consistent: an integral-valued float (5.0) is accepted and
converted for integer parameters, while a non-integral float (5.5) is
rejected with a TypeError instead of being silently truncated.

- MethodBinder: treat integral Python floats as implicit-conversion candidates
  for integer parameters (enums excluded).
- Converter.ToPrimitive: reject non-integral Python floats targeting integer
  types so truncation never happens silently.
- Add a shared Type.IsInteger() helper in Util and use it in both places.
- Add TestFloatToIntConversion covering single and overloaded ctor/method.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When argument binding fails, the raised TypeError only reported the argument
types that were passed, giving no clue what the method actually expected. For
example RangeConsolidator(5.5) produced:

    No method matches given arguments for .ctor: (<class 'float'>)

Append the candidate overload signatures to the message so the caller can see
what was expected (e.g. that an int overload exists when a float was passed).
This applies to every "no match" case, not just numeric conversions.

- Single candidate  -> ". The expected signature is:" + one signature.
- Multiple candidates -> ". The following overloads are available:" + list
  (distinct, capped at 10 with "... and N more").

Signatures are rendered readably: friendly type names (by-ref/nullable
unwrapped, generics as Name[Arg1, Arg2]), params marked, optional parameters
shown with their default. The whole hint is best-effort and wrapped in a
try/catch so it can never mask the original binding failure.

- MethodBinder: add AppendOverloads/FormatSignature/FormatType/FormatDefaultValue
  and emit the hint from Invoke's no-binding path.
- Add message tests to TestFloatToIntConversion (single and multiple overloads).
- Update TestCallbacks.TestNoOverloadException: the argument types are no longer
  at the end of the message, so assert containment instead of suffix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "No method matches" hint now uses the snake_case names Python callers
actually use, both in the message header and in each candidate signature:

    No method matches given arguments for compute_scaled: (<class 'float'>). The expected signature is:
      compute_scaled(Int32 scale_factor)

- Add SnakeCaseName(MethodBase) and use it for the header and FormatSignature
  (constructors keep their special .ctor token).
- Snake_case parameter names in FormatSignature via Name.ToSnakeCase().
- Add tests: method name and parameter names are snake_cased for single and
  multiple overloads.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jhonabreul jhonabreul changed the title Accept integral floats for int params and hint overloads on bind failure Accept integral floats for int params, hint overloads on bind failure (2.0.57) Jul 2, 2026
Bump AssemblyVersion/AssemblyFileVersion and the perf-test baseline
reference to 2.0.57 to match the package <Version>.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jhonabreul jhonabreul force-pushed the bug-integral-float-to-int-conversion branch from 189ef5f to e3640ac Compare July 2, 2026 22:00
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.

1 participant