Skip to content

Conversation

gentlegiantJGC
Copy link
Contributor

@gentlegiantJGC gentlegiantJGC commented Sep 16, 2025

Description

The casting rules and type hints for py::float_ and py::int_ should match the python type hint conventions.
An input typed with int can only accept int types and float can accept float or int types as defined in as per PEP 484.
when an argument is annotated as having type float, an argument of type int is acceptable

Expected behaviour

Class can cast from type hint
py::int_ int int
py::float_ float or int float

Current behavour

Class can cast from type hint
py::int_ int typing.SupportsInt
py::float_ float typing.SupportsFloat

The type hints were changed in #5540 by @InvincibleRMC but the classes do not support these casting rules.

m.def("test_int", [](py::int_ i) { return i; });
m.def("test_float", [](py::float_ f) { return f; });
test_int(5.5)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: test_int(): incompatible function arguments. The following argument types are supported:
    1. (arg0: typing.SupportsInt) -> int
Invoked with: 5.5
test_float(5)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: test_float(): incompatible function arguments. The following argument types are supported:
    1. (arg0: typing.SupportsFloat) -> float
Invoked with: 5

Fixes #5840

Suggested changelog entry:

  • Fix py::float_ casting and py::int_ and py::float_ type hints

These two types do not support casting from int-like and float-like types.
@gentlegiantJGC
Copy link
Contributor Author

gentlegiantJGC commented Sep 16, 2025

I feel like py::float_ should handle automatically casting from a python int as per PEP 484
when an argument is annotated as having type float, an argument of type int is acceptable
But that should be another pull request.

Edit: After debugging this a little py::float_ can be constructed from a py::int_ but the type caster will only cast if the type is an instance of float

gentlegiantJGC and others added 2 commits September 16, 2025 11:22
The default py::object caster only works if the object is an instance of the type.
py::float_ should accept python int objects as well as float.
This caster will pass through float as usual and cast int to float.
The caster handles the type name so the custom one is not required.
@gentlegiantJGC gentlegiantJGC changed the title Revert type hint changes to int_ and float_ Add float type caster and revert type hint changes to int_ and float_ Sep 16, 2025
@gentlegiantJGC
Copy link
Contributor Author

I don't know what has happend with those tests but I feel it is beyond the scope of the changes I have made.

@InvincibleRMC
Copy link
Contributor

Interesting py::float_ doesn't convert the same as float. Nice fix!

Copy link
Collaborator

@rwgk rwgk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. @InvincibleRMC could you please also review & formally approve? I'll merge this PR after I see your approval.

m.def("get_tuple_from_iterable", [](const py::iterable &iter) { return py::tuple(iter); });
// test_float
m.def("get_float", [] { return py::float_(0.0f); });
m.def("cast_float", [](py::float_ f) { return f; });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please rename this to roundtrip_float or float_roundtrip or similar?

(cast is super ambiguous in the context pybind11; C/C++ cast, to-Python, from-Python, all in the same name bucket unfortunately)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@gentlegiantJGC
Copy link
Contributor Author

gentlegiantJGC commented Sep 26, 2025

@rwgk do you have any idea what is causing the tests to fail?
I can't see what I have changed that would cause the segfault

These are the failing tests
macos-latest, graalpy-24.2 https://github.com/pybind/pybind11/actions/runs/18040422028/job/51337588233
ubuntu-latest, graalpy-24.1 https://github.com/pybind/pybind11/actions/runs/18040422028/job/51337588534

@rwgk
Copy link
Collaborator

rwgk commented Sep 26, 2025

@msimacek Could you please take a look at these graalpy failures? We believe they are unrelated to this PR.

https://github.com/pybind/pybind11/actions/runs/17764708439/job/50485236713

Copy link
Contributor

@InvincibleRMC InvincibleRMC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Nice fix.

@rwgk
Copy link
Collaborator

rwgk commented Sep 26, 2025

I'm using PR #3939 to see if current master has the same issue with the two graalpy tests.

@gentlegiantJGC
Copy link
Contributor Author

I'm using PR #3939 to see if current master has the same issue with the two graalpy tests.

Looks like it only ran half the tests and not the ones that failed here.

@rwgk
Copy link
Collaborator

rwgk commented Sep 26, 2025

I'm using PR #3939 to see if current master has the same issue with the two graalpy tests.

Looks like it only ran half the tests and not the ones that failed here.

Yeah :-(
I forgot about that. I converted to Ready for review to trick it into running everything.

@rwgk
Copy link
Collaborator

rwgk commented Sep 26, 2025

I just manually cancelled the workflow, to not block those two runner slots for several more hours until the tests hit the timeout.

@rwgk
Copy link
Collaborator

rwgk commented Sep 26, 2025

CI passes under #3939, but I'm a bit confused at the minute TBH:

  • 3939 — All checks have passed 3 skipped, 76 successful checks

  • Here — Some checks haven't completed yet, 2 cancelled, 2 skipped, 81 successful checks

So that's 79 total vs 85

I need to look more carefully, probably I'll get a chance only tomorrow or so. If there is anything you see, please let me know.

@henryiii JIC that's something obvious to you

@gentlegiantJGC
Copy link
Contributor Author

The tests that failed here have passed in your PR. It must be something I changed but I have no idea what it is.

The output is the same up to this line

 ../../tests/test_pytypes.py::test_surrogate_pairs_unicode_error[str_from_std_string_input] XFAIL [ 76%]

This is the first test that did not run

../../tests/test_pytypes.py::test_bytes PASSED                           [ 76%]

Are your tests run sequentially? Is this where it is failing?

The only potential break I can see is the removal of handle_type_name for float_.
I thought it was not needed with it added to the custom type_caster but perhaps something needs it?

@gentlegiantJGC
Copy link
Contributor Author

I will try running the tests tomorrow in my branch and see where it breaks

@rwgk
Copy link
Collaborator

rwgk commented Sep 26, 2025

Are your tests run sequentially?

Sorry I'm not sure I understand the question. I did not run anything locally, or change anything in PR #3939, except changing one character in the main README. I.e. all tests should run identically to your PR. But then again, there is the difference in the total number of tests that's still a mystery to me.

@rwgk
Copy link
Collaborator

rwgk commented Sep 26, 2025

Trying another super quick and easy thing: PR #5850

Just started. But now I see already that the total number of tests is 85, like here.

@rwgk
Copy link
Collaborator

rwgk commented Sep 26, 2025

All 83 tests pass under #5850: it seems very certain now that there is a problem with this PR, unfortunately, or maybe this PR is triggering a latent bug in graalpy.

@gentlegiantJGC I think it could save you time (potentially a lot) if you wait until we hear back from @msimacek.

@gentlegiantJGC
Copy link
Contributor Author

It was a reference counting mistake I had made.
I have fixed that and simplified the implementation a bit.

Another test is now stuck indefinetly. No error for this one though.

You may want to add a more sensible maximum time limit for workflows.
The default of 6 hours is a bit high.
https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idtimeout-minutes

@rwgk
Copy link
Collaborator

rwgk commented Sep 27, 2025

Another test is now stuck indefinetly. No error for this one though.

I think I have seen that one a few times before, most likely it's unrelated to this PR. I just cancelled the job and then triggered a rerun.

You may want to add a more sensible maximum time limit for workflows. The default of 6 hours is a bit high. https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idtimeout-minutes

It'd be awesome if you could send a PR for that. 90 minutes will probably never time out unless there is an actual bug.

@rwgk
Copy link
Collaborator

rwgk commented Sep 27, 2025

It was a reference counting mistake I had made.

Ah, I missed that there was a reinterpret_steal before. Interesting that it didn't crash on any other platforms.

@rwgk rwgk merged commit 8ed0dab into pybind:master Sep 27, 2025
147 of 148 checks passed
@github-actions github-actions bot added the needs changelog Possibly needs a changelog entry label Sep 27, 2025
@gentlegiantJGC gentlegiantJGC deleted the fix-int-float-type-hint branch September 27, 2025 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs changelog Possibly needs a changelog entry
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG]: py::float_ does not support casting from python int
3 participants