From 60e85284892727dff483a88b198ae374b9ad30e1 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 16 Sep 2025 08:46:49 +0100 Subject: [PATCH 01/10] Revert type hint changes to int_ and float_ These two types do not support casting from int-like and float-like types. --- include/pybind11/cast.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index c635791fee..ad7324a4c2 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1400,7 +1400,7 @@ struct handle_type_name { }; template <> struct handle_type_name { - static constexpr auto name = io_name("typing.SupportsInt", "int"); + static constexpr auto name = const_name("int"); }; template <> struct handle_type_name { @@ -1412,7 +1412,7 @@ struct handle_type_name { }; template <> struct handle_type_name { - static constexpr auto name = io_name("typing.SupportsFloat", "float"); + static constexpr auto name = const_name("float"); }; template <> struct handle_type_name { From b8a619d8fa41dbec825330366273a5d379145e87 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 16 Sep 2025 09:09:49 +0100 Subject: [PATCH 02/10] Fix tests --- tests/test_pytypes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index a199d72f0a..8c50bfc277 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -917,7 +917,7 @@ def test_inplace_rshift(a, b): def test_tuple_nonempty_annotations(doc): assert ( doc(m.annotate_tuple_float_str) - == "annotate_tuple_float_str(arg0: tuple[typing.SupportsFloat, str]) -> None" + == "annotate_tuple_float_str(arg0: tuple[float, str]) -> None" ) @@ -930,7 +930,7 @@ def test_tuple_empty_annotations(doc): def test_tuple_variable_length_annotations(doc): assert ( doc(m.annotate_tuple_variable_length) - == "annotate_tuple_variable_length(arg0: tuple[typing.SupportsFloat, ...]) -> None" + == "annotate_tuple_variable_length(arg0: tuple[float, ...]) -> None" ) @@ -989,7 +989,7 @@ def test_type_annotation(doc): def test_union_annotations(doc): assert ( doc(m.annotate_union) - == "annotate_union(arg0: list[str | typing.SupportsInt | object], arg1: str, arg2: typing.SupportsInt, arg3: object) -> list[str | int | object]" + == "annotate_union(arg0: list[str | int | object], arg1: str, arg2: int, arg3: object) -> list[str | int | object]" ) From 650c3ee416322be255bf15e2585ce357f741641d Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 16 Sep 2025 11:22:14 +0100 Subject: [PATCH 03/10] Add a custom py::float_ caster 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. --- include/pybind11/cast.h | 27 +++++++++++++++++++++++---- tests/test_pytypes.cpp | 1 + tests/test_pytypes.py | 7 +++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index ad7324a4c2..03dbd7bae2 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1411,10 +1411,6 @@ struct handle_type_name { static constexpr auto name = const_name("collections.abc.Iterator"); }; template <> -struct handle_type_name { - static constexpr auto name = const_name("float"); -}; -template <> struct handle_type_name { static constexpr auto name = const_name("collections.abc.Callable"); }; @@ -1533,6 +1529,29 @@ struct pyobject_caster { template class type_caster::value>> : public pyobject_caster {}; +template <> +struct type_caster { + PYBIND11_TYPE_CASTER(float_, const_name("float")); + + bool load(handle src, bool /* convert */) + { + if (isinstance(src)) { + value = reinterpret_borrow(src); + return true; + } else if (isinstance(src)) { + value = float_(reinterpret_steal(src)); + return true; + } else { + return false; + } + } + + static handle cast(const handle& src, return_value_policy /* policy */, handle /* parent */) + { + return src.inc_ref(); + } +}; + // Our conditions for enabling moving are quite restrictive: // At compile time: // - T needs to be a non-const, non-pointer, non-reference type diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 1c136cf0f2..9aa7ad7a2d 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -209,6 +209,7 @@ TEST_SUBMODULE(pytypes, m) { 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; }); // test_list m.def("list_no_args", []() { return py::list{}; }); m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; }); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index 8c50bfc277..fd23830dbe 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -61,6 +61,13 @@ def test_iterable(doc): def test_float(doc): assert doc(m.get_float) == "get_float() -> float" + assert doc(m.cast_float) == "get_float(arg0: float) -> float" + f1 = m.cast_float(5.5) + assert isinstance(f1, float) + assert f1 == 5.5 + f2 = m.cast_float(5) + assert isinstance(f2, float) + assert f1 == 5.0 def test_list(capture, doc): From 4bab56741ba6633ae87fef1ea74a64e6d39f0731 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:23:05 +0000 Subject: [PATCH 04/10] style: pre-commit fixes --- include/pybind11/cast.h | 6 ++---- tests/test_pytypes.cpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 03dbd7bae2..77477cf5cb 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1533,8 +1533,7 @@ template <> struct type_caster { PYBIND11_TYPE_CASTER(float_, const_name("float")); - bool load(handle src, bool /* convert */) - { + bool load(handle src, bool /* convert */) { if (isinstance(src)) { value = reinterpret_borrow(src); return true; @@ -1546,8 +1545,7 @@ struct type_caster { } } - static handle cast(const handle& src, return_value_policy /* policy */, handle /* parent */) - { + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { return src.inc_ref(); } }; diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index 9aa7ad7a2d..c6f33ad8ab 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -209,7 +209,7 @@ TEST_SUBMODULE(pytypes, m) { 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; }); + m.def("cast_float", [](py::float_ f) { return f; }); // test_list m.def("list_no_args", []() { return py::list{}; }); m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; }); From 35948cec0b3c131c64102926fccd20325f039d7a Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 16 Sep 2025 11:41:11 +0100 Subject: [PATCH 05/10] Fix name --- tests/test_pytypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index fd23830dbe..b503649694 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -61,7 +61,7 @@ def test_iterable(doc): def test_float(doc): assert doc(m.get_float) == "get_float() -> float" - assert doc(m.cast_float) == "get_float(arg0: float) -> float" + assert doc(m.cast_float) == "cast_float(arg0: float) -> float" f1 = m.cast_float(5.5) assert isinstance(f1, float) assert f1 == 5.5 From a8a4d001ff10c060860b39b1df3fb970f2c24be0 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 16 Sep 2025 11:47:31 +0100 Subject: [PATCH 06/10] Fix variable --- tests/test_pytypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index b503649694..fda6491c6c 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -67,7 +67,7 @@ def test_float(doc): assert f1 == 5.5 f2 = m.cast_float(5) assert isinstance(f2, float) - assert f1 == 5.0 + assert f2 == 5.0 def test_list(capture, doc): From 4b694ec80e5674d5283b6fd11db8328072faed52 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Tue, 16 Sep 2025 12:46:28 +0100 Subject: [PATCH 07/10] Try satisfying the formatter --- include/pybind11/cast.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 77477cf5cb..18aacfec7a 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1536,13 +1536,12 @@ struct type_caster { bool load(handle src, bool /* convert */) { if (isinstance(src)) { value = reinterpret_borrow(src); - return true; } else if (isinstance(src)) { value = float_(reinterpret_steal(src)); - return true; } else { return false; } + return true; } static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { From 53ca8a9aee4b251681b11da8eb802e62b58945ac Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Fri, 26 Sep 2025 16:25:53 +0100 Subject: [PATCH 08/10] Rename test function --- tests/test_pytypes.cpp | 2 +- tests/test_pytypes.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index c6f33ad8ab..7d5423e549 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -209,7 +209,7 @@ TEST_SUBMODULE(pytypes, m) { 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; }); + m.def("float_roundtrip", [](py::float_ f) { return f; }); // test_list m.def("list_no_args", []() { return py::list{}; }); m.def("list_ssize_t", []() { return py::list{(py::ssize_t) 0}; }); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index fda6491c6c..c1798f924c 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -61,11 +61,11 @@ def test_iterable(doc): def test_float(doc): assert doc(m.get_float) == "get_float() -> float" - assert doc(m.cast_float) == "cast_float(arg0: float) -> float" - f1 = m.cast_float(5.5) + assert doc(m.float_roundtrip) == "float_roundtrip(arg0: float) -> float" + f1 = m.float_roundtrip(5.5) assert isinstance(f1, float) assert f1 == 5.5 - f2 = m.cast_float(5) + f2 = m.float_roundtrip(5) assert isinstance(f2, float) assert f2 == 5.0 From 46e9e7478a36e8908c5f6063e0a39d6c6b7d98a5 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 27 Sep 2025 14:57:26 +0100 Subject: [PATCH 09/10] Simplify type caster --- include/pybind11/cast.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index afc14aafe5..e3e4b48a2e 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1412,6 +1412,10 @@ struct handle_type_name { static constexpr auto name = const_name("collections.abc.Iterator"); }; template <> +struct handle_type_name { + static constexpr auto name = const_name("float"); +}; +template <> struct handle_type_name { static constexpr auto name = const_name("collections.abc.Callable"); }; @@ -1531,9 +1535,8 @@ template class type_caster::value>> : public pyobject_caster {}; template <> -struct type_caster { - PYBIND11_TYPE_CASTER(float_, const_name("float")); - +class type_caster : public pyobject_caster { +public: bool load(handle src, bool /* convert */) { if (isinstance(src)) { value = reinterpret_borrow(src); @@ -1544,10 +1547,6 @@ struct type_caster { } return true; } - - static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { - return src.inc_ref(); - } }; // Our conditions for enabling moving are quite restrictive: From 50e81a9821a997fb8595f3c44c84a11f7c6bf645 Mon Sep 17 00:00:00 2001 From: gentlegiantJGC Date: Sat, 27 Sep 2025 14:57:53 +0100 Subject: [PATCH 10/10] Fix reference counting issue --- include/pybind11/cast.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index e3e4b48a2e..7b014fed99 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1541,7 +1541,7 @@ class type_caster : public pyobject_caster { if (isinstance(src)) { value = reinterpret_borrow(src); } else if (isinstance(src)) { - value = float_(reinterpret_steal(src)); + value = float_(reinterpret_borrow(src)); } else { return false; }