From 4d8b37688cbd677112503d0cc314c12d6fc7bb2d Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Sat, 29 Jun 2024 11:57:10 -0400 Subject: [PATCH 01/17] tests passing --- include/pybind11/cast.h | 6 ++++++ include/pybind11/eigen/matrix.h | 4 ++-- include/pybind11/eigen/tensor.h | 2 +- include/pybind11/numpy.h | 2 +- tests/test_eigen_matrix.py | 22 +++++++++++----------- tests/test_eigen_tensor.py | 14 +++++++------- tests/test_numpy_array.py | 20 ++++++++++---------- tests/test_numpy_dtypes.py | 2 +- tests/test_numpy_vectorize.py | 10 +++++----- 9 files changed, 44 insertions(+), 38 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 624b8ebaca..a0b83ab625 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -740,6 +740,12 @@ class type_caster> : public tuple_caster {} template class type_caster> : public tuple_caster {}; +template <> +class type_caster> : public tuple_caster { +public: + static constexpr auto name = const_name("tuple[()]"); +}; + /// Helper class which abstracts away certain actions. Users can provide specializations for /// custom holders, but it's only necessary if the type has a non-standard interface. template diff --git a/include/pybind11/eigen/matrix.h b/include/pybind11/eigen/matrix.h index 8d4342f81b..098727f5a8 100644 --- a/include/pybind11/eigen/matrix.h +++ b/include/pybind11/eigen/matrix.h @@ -224,7 +224,7 @@ struct EigenProps { = !show_c_contiguous && show_order && requires_col_major; static constexpr auto descriptor - = const_name("numpy.ndarray[") + npy_format_descriptor::name + const_name("[") + = const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("[") + const_name(const_name<(size_t) rows>(), const_name("m")) + const_name(", ") + const_name(const_name<(size_t) cols>(), const_name("n")) + const_name("]") + @@ -232,7 +232,7 @@ struct EigenProps { // be satisfied: writeable=True (for a mutable reference), and, depending on the map's // stride options, possibly f_contiguous or c_contiguous. We include them in the // descriptor output to provide some hint as to why a TypeError is occurring (otherwise - // it can be confusing to see that a function accepts a 'numpy.ndarray[float64[3,2]]' and + // it can be confusing to see that a function accepts a 'numpy.typing.NDArray[float64[3,2]]' and // an error message that you *gave* a numpy.ndarray of the right type and dimensions. const_name(", flags.writeable", "") + const_name(", flags.c_contiguous", "") diff --git a/include/pybind11/eigen/tensor.h b/include/pybind11/eigen/tensor.h index d4ed6c0ca8..b9f1736f54 100644 --- a/include/pybind11/eigen/tensor.h +++ b/include/pybind11/eigen/tensor.h @@ -127,7 +127,7 @@ struct get_tensor_descriptor { + const_name(Type::Layout) == static_cast(Eigen::RowMajor)>( ", flags.c_contiguous", ", flags.f_contiguous"); static constexpr auto value - = const_name("numpy.ndarray[") + npy_format_descriptor::name + = const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("[") + eigen_tensor_helper>::dimensions_descriptor + const_name("]") + const_name(details, const_name("")) + const_name("]"); }; diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 05ef3918b1..b30acba65a 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -2086,7 +2086,7 @@ vectorize_helper vectorize_extractor(const Func &f, Retur template struct handle_type_name> { static constexpr auto name - = const_name("numpy.ndarray[") + npy_format_descriptor::name + const_name("]"); + = const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("]"); }; PYBIND11_NAMESPACE_END(detail) diff --git a/tests/test_eigen_matrix.py b/tests/test_eigen_matrix.py index e1d7433f15..6eee6d6616 100644 --- a/tests/test_eigen_matrix.py +++ b/tests/test_eigen_matrix.py @@ -94,18 +94,18 @@ def test_mutator_descriptors(): with pytest.raises(TypeError) as excinfo: m.fixed_mutator_r(zc) assert ( - "(arg0: numpy.ndarray[numpy.float32[5, 6]," + "(arg0: numpy.typing.NDArray[numpy.float32[5, 6]," " flags.writeable, flags.c_contiguous]) -> None" in str(excinfo.value) ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_c(zr) assert ( - "(arg0: numpy.ndarray[numpy.float32[5, 6]," + "(arg0: numpy.typing.NDArray[numpy.float32[5, 6]," " flags.writeable, flags.f_contiguous]) -> None" in str(excinfo.value) ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype="float32")) - assert "(arg0: numpy.ndarray[numpy.float32[5, 6], flags.writeable]) -> None" in str( + assert "(arg0: numpy.typing.NDArray[numpy.float32[5, 6], flags.writeable]) -> None" in str( excinfo.value ) zr.flags.writeable = False @@ -200,7 +200,7 @@ def test_negative_stride_from_python(msg): msg(excinfo.value) == """ double_threer(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None + 1. (arg0: numpy.typing.NDArray[numpy.float32[1, 3], flags.writeable]) -> None Invoked with: """ + repr(np.array([5.0, 4.0, 3.0], dtype="float32")) @@ -212,7 +212,7 @@ def test_negative_stride_from_python(msg): msg(excinfo.value) == """ double_threec(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None + 1. (arg0: numpy.typing.NDArray[numpy.float32[3, 1], flags.writeable]) -> None Invoked with: """ + repr(np.array([7.0, 4.0, 1.0], dtype="float32")) @@ -697,25 +697,25 @@ def test_dense_signature(doc): assert ( doc(m.double_col) == """ - double_col(arg0: numpy.ndarray[numpy.float32[m, 1]]) -> numpy.ndarray[numpy.float32[m, 1]] + double_col(arg0: numpy.typing.NDArray[numpy.float32[m, 1]]) -> numpy.typing.NDArray[numpy.float32[m, 1]] """ ) assert ( doc(m.double_row) == """ - double_row(arg0: numpy.ndarray[numpy.float32[1, n]]) -> numpy.ndarray[numpy.float32[1, n]] + double_row(arg0: numpy.typing.NDArray[numpy.float32[1, n]]) -> numpy.typing.NDArray[numpy.float32[1, n]] """ ) assert doc(m.double_complex) == ( """ - double_complex(arg0: numpy.ndarray[numpy.complex64[m, 1]])""" - """ -> numpy.ndarray[numpy.complex64[m, 1]] + double_complex(arg0: numpy.typing.NDArray[numpy.complex64[m, 1]])""" + """ -> numpy.typing.NDArray[numpy.complex64[m, 1]] """ ) assert doc(m.double_mat_rm) == ( """ - double_mat_rm(arg0: numpy.ndarray[numpy.float32[m, n]])""" - """ -> numpy.ndarray[numpy.float32[m, n]] + double_mat_rm(arg0: numpy.typing.NDArray[numpy.float32[m, n]])""" + """ -> numpy.typing.NDArray[numpy.float32[m, n]] """ ) diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py index a2b99d9d7d..7379a2981e 100644 --- a/tests/test_eigen_tensor.py +++ b/tests/test_eigen_tensor.py @@ -268,23 +268,23 @@ def test_round_trip_references_actually_refer(m): @pytest.mark.parametrize("m", submodules) def test_doc_string(m, doc): assert ( - doc(m.copy_tensor) == "copy_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]" + doc(m.copy_tensor) == "copy_tensor() -> numpy.typing.NDArray[numpy.float64[?, ?, ?]]" ) assert ( doc(m.copy_fixed_tensor) - == "copy_fixed_tensor() -> numpy.ndarray[numpy.float64[3, 5, 2]]" + == "copy_fixed_tensor() -> numpy.typing.NDArray[numpy.float64[3, 5, 2]]" ) assert ( doc(m.reference_const_tensor) - == "reference_const_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]" + == "reference_const_tensor() -> numpy.typing.NDArray[numpy.float64[?, ?, ?]]" ) order_flag = f"flags.{m.needed_options.lower()}_contiguous" assert doc(m.round_trip_view_tensor) == ( - f"round_trip_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])" - f" -> numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]" + f"round_trip_view_tensor(arg0: numpy.typing.NDArray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])" + f" -> numpy.typing.NDArray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]" ) assert doc(m.round_trip_const_view_tensor) == ( - f"round_trip_const_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], {order_flag}])" - " -> numpy.ndarray[numpy.float64[?, ?, ?]]" + f"round_trip_const_view_tensor(arg0: numpy.typing.NDArray[numpy.float64[?, ?, ?], {order_flag}])" + " -> numpy.typing.NDArray[numpy.float64[?, ?, ?]]" ) diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 4726a8e73c..66eaf6c79d 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -320,13 +320,13 @@ def test_overload_resolution(msg): msg(excinfo.value) == """ overloaded(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[numpy.float64]) -> str - 2. (arg0: numpy.ndarray[numpy.float32]) -> str - 3. (arg0: numpy.ndarray[numpy.int32]) -> str - 4. (arg0: numpy.ndarray[numpy.uint16]) -> str - 5. (arg0: numpy.ndarray[numpy.int64]) -> str - 6. (arg0: numpy.ndarray[numpy.complex128]) -> str - 7. (arg0: numpy.ndarray[numpy.complex64]) -> str + 1. (arg0: numpy.typing.NDArray[numpy.float64]) -> str + 2. (arg0: numpy.typing.NDArray[numpy.float32]) -> str + 3. (arg0: numpy.typing.NDArray[numpy.int32]) -> str + 4. (arg0: numpy.typing.NDArray[numpy.uint16]) -> str + 5. (arg0: numpy.typing.NDArray[numpy.int64]) -> str + 6. (arg0: numpy.typing.NDArray[numpy.complex128]) -> str + 7. (arg0: numpy.typing.NDArray[numpy.complex64]) -> str Invoked with: 'not an array' """ @@ -342,8 +342,8 @@ def test_overload_resolution(msg): assert m.overloaded3(np.array([1], dtype="intc")) == "int" expected_exc = """ overloaded3(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.ndarray[numpy.int32]) -> str - 2. (arg0: numpy.ndarray[numpy.float64]) -> str + 1. (arg0: numpy.typing.NDArray[numpy.int32]) -> str + 2. (arg0: numpy.typing.NDArray[numpy.float64]) -> str Invoked with: """ @@ -527,7 +527,7 @@ def test_index_using_ellipsis(): ], ) def test_format_descriptors_for_floating_point_types(test_func): - assert "numpy.ndarray[numpy.float" in test_func.__doc__ + assert "numpy.typing.NDArray[numpy.float" in test_func.__doc__ @pytest.mark.parametrize("forcecast", [False, True]) diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 8ae239ed86..f813d96e11 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -351,7 +351,7 @@ def test_complex_array(): def test_signature(doc): assert ( doc(m.create_rec_nested) - == "create_rec_nested(arg0: int) -> numpy.ndarray[NestedStruct]" + == "create_rec_nested(arg0: int) -> numpy.typing.NDArray[NestedStruct]" ) diff --git a/tests/test_numpy_vectorize.py b/tests/test_numpy_vectorize.py index ce38d72d96..55b2496b29 100644 --- a/tests/test_numpy_vectorize.py +++ b/tests/test_numpy_vectorize.py @@ -150,7 +150,7 @@ def test_docs(doc): assert ( doc(m.vectorized_func) == """ - vectorized_func(arg0: numpy.ndarray[numpy.int32], arg1: numpy.ndarray[numpy.float32], arg2: numpy.ndarray[numpy.float64]) -> object + vectorized_func(arg0: numpy.typing.NDArray[numpy.int32], arg1: numpy.typing.NDArray[numpy.float32], arg2: numpy.typing.NDArray[numpy.float64]) -> object """ ) @@ -212,12 +212,12 @@ def test_passthrough_arguments(doc): + ", ".join( [ "arg0: float", - "arg1: numpy.ndarray[numpy.float64]", - "arg2: numpy.ndarray[numpy.float64]", - "arg3: numpy.ndarray[numpy.int32]", + "arg1: numpy.typing.NDArray[numpy.float64]", + "arg2: numpy.typing.NDArray[numpy.float64]", + "arg3: numpy.typing.NDArray[numpy.int32]", "arg4: int", "arg5: m.numpy_vectorize.NonPODClass", - "arg6: numpy.ndarray[numpy.float64]", + "arg6: numpy.typing.NDArray[numpy.float64]", ] ) + ") -> object" From 0645585d2d363493a2691ff1563322c532edf5ba Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Sat, 29 Jun 2024 11:58:34 -0400 Subject: [PATCH 02/17] lint --- include/pybind11/eigen/matrix.h | 13 +++++++------ tests/test_eigen_matrix.py | 5 +++-- tests/test_eigen_tensor.py | 3 ++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/pybind11/eigen/matrix.h b/include/pybind11/eigen/matrix.h index 098727f5a8..47c7b9bbed 100644 --- a/include/pybind11/eigen/matrix.h +++ b/include/pybind11/eigen/matrix.h @@ -224,16 +224,17 @@ struct EigenProps { = !show_c_contiguous && show_order && requires_col_major; static constexpr auto descriptor - = const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("[") - + const_name(const_name<(size_t) rows>(), const_name("m")) + const_name(", ") - + const_name(const_name<(size_t) cols>(), const_name("n")) + const_name("]") - + + = const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + + const_name("[") + const_name(const_name<(size_t) rows>(), const_name("m")) + + const_name(", ") + const_name(const_name<(size_t) cols>(), const_name("n")) + + const_name("]") + // For a reference type (e.g. Ref) we have other constraints that might need to // be satisfied: writeable=True (for a mutable reference), and, depending on the map's // stride options, possibly f_contiguous or c_contiguous. We include them in the // descriptor output to provide some hint as to why a TypeError is occurring (otherwise - // it can be confusing to see that a function accepts a 'numpy.typing.NDArray[float64[3,2]]' and - // an error message that you *gave* a numpy.ndarray of the right type and dimensions. + // it can be confusing to see that a function accepts a + // 'numpy.typing.NDArray[float64[3,2]]' and an error message that you *gave* a + // numpy.ndarray of the right type and dimensions. const_name(", flags.writeable", "") + const_name(", flags.c_contiguous", "") + const_name(", flags.f_contiguous", "") + const_name("]"); diff --git a/tests/test_eigen_matrix.py b/tests/test_eigen_matrix.py index 6eee6d6616..29e247514d 100644 --- a/tests/test_eigen_matrix.py +++ b/tests/test_eigen_matrix.py @@ -105,8 +105,9 @@ def test_mutator_descriptors(): ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype="float32")) - assert "(arg0: numpy.typing.NDArray[numpy.float32[5, 6], flags.writeable]) -> None" in str( - excinfo.value + assert ( + "(arg0: numpy.typing.NDArray[numpy.float32[5, 6], flags.writeable]) -> None" + in str(excinfo.value) ) zr.flags.writeable = False with pytest.raises(TypeError): diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py index 7379a2981e..4723bb4296 100644 --- a/tests/test_eigen_tensor.py +++ b/tests/test_eigen_tensor.py @@ -268,7 +268,8 @@ def test_round_trip_references_actually_refer(m): @pytest.mark.parametrize("m", submodules) def test_doc_string(m, doc): assert ( - doc(m.copy_tensor) == "copy_tensor() -> numpy.typing.NDArray[numpy.float64[?, ?, ?]]" + doc(m.copy_tensor) + == "copy_tensor() -> numpy.typing.NDArray[numpy.float64[?, ?, ?]]" ) assert ( doc(m.copy_fixed_tensor) From bbd574afa5df9ee1e3e7571b520cd01dba51ee7c Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Sat, 29 Jun 2024 12:10:31 -0400 Subject: [PATCH 03/17] add comment --- include/pybind11/cast.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index a0b83ab625..e41ad2abfa 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -743,6 +743,7 @@ class type_caster> : public tuple_caster {} template <> class type_caster> : public tuple_caster { public: + // PEP 484 specifies this syntax for an empty tuple static constexpr auto name = const_name("tuple[()]"); }; From 6e5df0f99b834007ac67ad0ca056ac8f6262c528 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Sun, 30 Jun 2024 11:01:10 -0400 Subject: [PATCH 04/17] remove empty tuple[()] --- include/pybind11/cast.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index e41ad2abfa..624b8ebaca 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -740,13 +740,6 @@ class type_caster> : public tuple_caster {} template class type_caster> : public tuple_caster {}; -template <> -class type_caster> : public tuple_caster { -public: - // PEP 484 specifies this syntax for an empty tuple - static constexpr auto name = const_name("tuple[()]"); -}; - /// Helper class which abstracts away certain actions. Users can provide specializations for /// custom holders, but it's only necessary if the type has a non-standard interface. template From cbb4bca877ffe3d72683d737818721d5ad2dbd08 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 24 Jan 2025 23:27:30 -0500 Subject: [PATCH 05/17] test io_name --- include/pybind11/numpy.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 4a56772ad1..c9e0f67aee 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -175,7 +175,7 @@ inline numpy_internals &get_numpy_internals() { PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) { module_ numpy = module_::import("numpy"); str version_string = numpy.attr("__version__"); - +> module_ numpy_lib = module_::import("numpy.lib"); object numpy_version = numpy_lib.attr("NumpyVersion")(version_string); int major_version = numpy_version.attr("major").cast(); @@ -1445,7 +1445,8 @@ struct pyobject_caster> { static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { return src.inc_ref(); } - PYBIND11_TYPE_CASTER(type, handle_type_name::name); + PYBIND11_TYPE_CASTER(type, + io_name(const_name("numpy.typing.ArrayLike]"), const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("]"))); }; template From b9ee9c4796e5963c2a94900ca228e30568e18bca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 04:27:53 +0000 Subject: [PATCH 06/17] style: pre-commit fixes --- include/pybind11/numpy.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index c9e0f67aee..3f0ca31e8e 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -175,8 +175,7 @@ inline numpy_internals &get_numpy_internals() { PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) { module_ numpy = module_::import("numpy"); str version_string = numpy.attr("__version__"); -> - module_ numpy_lib = module_::import("numpy.lib"); + > module_ numpy_lib = module_::import("numpy.lib"); object numpy_version = numpy_lib.attr("NumpyVersion")(version_string); int major_version = numpy_version.attr("major").cast(); @@ -1446,7 +1445,9 @@ struct pyobject_caster> { return src.inc_ref(); } PYBIND11_TYPE_CASTER(type, - io_name(const_name("numpy.typing.ArrayLike]"), const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("]"))); + io_name(const_name("numpy.typing.ArrayLike]"), + const_name("numpy.typing.NDArray[") + + npy_format_descriptor::name + const_name("]"))); }; template From d08b3a8ab8eb095e663ab7b33147ce19640cb855 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 24 Jan 2025 23:32:14 -0500 Subject: [PATCH 07/17] remove accidental > Signed-off-by: Michael Carlstrom --- include/pybind11/numpy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 3f0ca31e8e..85e1f58d32 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -175,7 +175,7 @@ inline numpy_internals &get_numpy_internals() { PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) { module_ numpy = module_::import("numpy"); str version_string = numpy.attr("__version__"); - > module_ numpy_lib = module_::import("numpy.lib"); + module_ numpy_lib = module_::import("numpy.lib"); object numpy_version = numpy_lib.attr("NumpyVersion")(version_string); int major_version = numpy_version.attr("major").cast(); From 6c61c0079b6d6d27be3be5e81ef2656527b5fe62 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 24 Jan 2025 23:45:58 -0500 Subject: [PATCH 08/17] try T --- include/pybind11/numpy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 85e1f58d32..24d366633a 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1445,9 +1445,9 @@ struct pyobject_caster> { return src.inc_ref(); } PYBIND11_TYPE_CASTER(type, - io_name(const_name("numpy.typing.ArrayLike]"), + io_name("numpy.typing.ArrayLike", const_name("numpy.typing.NDArray[") - + npy_format_descriptor::name + const_name("]"))); + + npy_format_descriptor::name + const_name("]"))); }; template From 4e8490baaf011687c350637f136ef073941303d6 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 24 Jan 2025 23:49:10 -0500 Subject: [PATCH 09/17] make both const_name Signed-off-by: Michael Carlstrom --- include/pybind11/numpy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 24d366633a..065e7513ba 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1445,7 +1445,7 @@ struct pyobject_caster> { return src.inc_ref(); } PYBIND11_TYPE_CASTER(type, - io_name("numpy.typing.ArrayLike", + io_name(const_name("numpy.typing.ArrayLike"), const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("]"))); }; From 8e15be25436fdc55caf5825cc8ea7064659839a8 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 24 Jan 2025 23:53:54 -0500 Subject: [PATCH 10/17] try and treat as string --- include/pybind11/numpy.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 065e7513ba..de9ccdcfef 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1445,9 +1445,9 @@ struct pyobject_caster> { return src.inc_ref(); } PYBIND11_TYPE_CASTER(type, - io_name(const_name("numpy.typing.ArrayLike"), - const_name("numpy.typing.NDArray[") - + npy_format_descriptor::name + const_name("]"))); + io_name("numpy.typing.ArrayLike", + "numpy.typing.NDArray[" + + npy_format_descriptor::name + "]")); }; template From 60081277146d83b94f7dca88e4bdb8a83c48eab0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 04:54:17 +0000 Subject: [PATCH 11/17] style: pre-commit fixes --- include/pybind11/numpy.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index de9ccdcfef..3cbcd9bcdd 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1446,8 +1446,7 @@ struct pyobject_caster> { } PYBIND11_TYPE_CASTER(type, io_name("numpy.typing.ArrayLike", - "numpy.typing.NDArray[" - + npy_format_descriptor::name + "]")); + "numpy.typing.NDArray[" + npy_format_descriptor::name + "]")); }; template From b2eb3371d18adb8f9dc9902e901c7b9a368ce95a Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 6 Feb 2025 17:35:18 -0500 Subject: [PATCH 12/17] Update Numpy type hints --- include/pybind11/eigen/matrix.h | 22 ++++++++++--------- include/pybind11/eigen/tensor.h | 13 ++++++----- include/pybind11/numpy.h | 6 ++---- tests/test_eigen_matrix.py | 38 ++++++++++++++++----------------- tests/test_eigen_tensor.py | 16 +++++++------- tests/test_numpy_array.py | 16 +++++++------- tests/test_numpy_vectorize.py | 10 ++++----- 7 files changed, 62 insertions(+), 59 deletions(-) diff --git a/include/pybind11/eigen/matrix.h b/include/pybind11/eigen/matrix.h index f1abd3824e..d8a594fdee 100644 --- a/include/pybind11/eigen/matrix.h +++ b/include/pybind11/eigen/matrix.h @@ -224,21 +224,23 @@ struct EigenProps { static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; - static constexpr auto descriptor - = const_name("numpy.typing.NDArray[") + npy_format_descriptor::name - + const_name("[") + const_name(const_name<(size_t) rows>(), const_name("m")) - + const_name(", ") + const_name(const_name<(size_t) cols>(), const_name("n")) - + const_name("]") + + static constexpr auto descriptor + = const_name("typing.Annotated[") + + io_name("numpy.typing.ArrayLike, ", "numpy.typing.NDArray[") + + npy_format_descriptor::name + io_name("", "]") + const_name(", \"[") + + const_name(const_name<(size_t) rows>(), const_name("m")) + const_name(", ") + + const_name(const_name<(size_t) cols>(), const_name("n")) + + const_name("]\"") // For a reference type (e.g. Ref) we have other constraints that might need to // be satisfied: writeable=True (for a mutable reference), and, depending on the map's // stride options, possibly f_contiguous or c_contiguous. We include them in the // descriptor output to provide some hint as to why a TypeError is occurring (otherwise // it can be confusing to see that a function accepts a - // 'numpy.typing.NDArray[float64[3,2]]' and an error message that you *gave* a - // numpy.ndarray of the right type and dimensions. - const_name(", flags.writeable", "") - + const_name(", flags.c_contiguous", "") - + const_name(", flags.f_contiguous", "") + const_name("]"); + // 'typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3,2]"]' and an error message + // that you *gave* a numpy.ndarray of the right type and dimensions. + + const_name(", \"flags.writeable\"", "") + + const_name(", \"flags.c_contiguous\"", "") + + const_name(", \"flags.f_contiguous\"", "") + const_name("]"); }; // Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, diff --git a/include/pybind11/eigen/tensor.h b/include/pybind11/eigen/tensor.h index bf7726f3a4..b65f29b02e 100644 --- a/include/pybind11/eigen/tensor.h +++ b/include/pybind11/eigen/tensor.h @@ -124,13 +124,16 @@ struct eigen_tensor_helper< template struct get_tensor_descriptor { static constexpr auto details - = const_name(", flags.writeable", "") + const_name + = const_name(", \"flags.writeable\"", "") + const_name < static_cast(Type::Layout) - == static_cast(Eigen::RowMajor) > (", flags.c_contiguous", ", flags.f_contiguous"); + == static_cast(Eigen::RowMajor) + > (", \"flags.c_contiguous\"", ", \"flags.f_contiguous\""); static constexpr auto value - = const_name("numpy.typing.NDArray[") + npy_format_descriptor::name - + const_name("[") + eigen_tensor_helper>::dimensions_descriptor - + const_name("]") + const_name(details, const_name("")) + const_name("]"); + = const_name("typing.Annotated[") + + io_name("numpy.typing.ArrayLike, ", "numpy.typing.NDArray[") + + npy_format_descriptor::name + io_name("", "]") + + const_name(", \"[") + eigen_tensor_helper>::dimensions_descriptor + + const_name("]\"") + const_name(details, const_name("")) + const_name("]"); }; // When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes does not have the begin() member diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 3cbcd9bcdd..8280f19873 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1444,9 +1444,7 @@ struct pyobject_caster> { static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { return src.inc_ref(); } - PYBIND11_TYPE_CASTER(type, - io_name("numpy.typing.ArrayLike", - "numpy.typing.NDArray[" + npy_format_descriptor::name + "]")); + PYBIND11_TYPE_CASTER(type, handle_type_name::name); }; template @@ -2184,7 +2182,7 @@ vectorize_helper vectorize_extractor(const Func &f, Retur template struct handle_type_name> { static constexpr auto name - = const_name("numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("]"); + = io_name("typing.Annotated[numpy.typing.ArrayLike, ", "numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("]"); }; PYBIND11_NAMESPACE_END(detail) diff --git a/tests/test_eigen_matrix.py b/tests/test_eigen_matrix.py index 705b3772dc..47fad5d8c6 100644 --- a/tests/test_eigen_matrix.py +++ b/tests/test_eigen_matrix.py @@ -95,19 +95,19 @@ def test_mutator_descriptors(): with pytest.raises(TypeError) as excinfo: m.fixed_mutator_r(zc) assert ( - "(arg0: numpy.typing.NDArray[numpy.float32[5, 6]," - " flags.writeable, flags.c_contiguous]) -> None" in str(excinfo.value) + "(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[5, 6]\"," + " \"flags.writeable\", \"flags.c_contiguous\"]) -> None" in str(excinfo.value) ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_c(zr) assert ( - "(arg0: numpy.typing.NDArray[numpy.float32[5, 6]," - " flags.writeable, flags.f_contiguous]) -> None" in str(excinfo.value) + "(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[5, 6]\"," + " \"flags.writeable\", \"flags.f_contiguous\"]) -> None" in str(excinfo.value) ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype="float32")) assert ( - "(arg0: numpy.typing.NDArray[numpy.float32[5, 6], flags.writeable]) -> None" + "(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[5, 6]\", \"flags.writeable\"]) -> None" in str(excinfo.value) ) zr.flags.writeable = False @@ -202,7 +202,7 @@ def test_negative_stride_from_python(msg): msg(excinfo.value) == """ double_threer(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.typing.NDArray[numpy.float32[1, 3], flags.writeable]) -> None + 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[1, 3]\", \"flags.writeable\"]) -> None Invoked with: """ + repr(np.array([5.0, 4.0, 3.0], dtype="float32")) @@ -214,7 +214,7 @@ def test_negative_stride_from_python(msg): msg(excinfo.value) == """ double_threec(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.typing.NDArray[numpy.float32[3, 1], flags.writeable]) -> None + 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[3, 1]\", \"flags.writeable\"]) -> None Invoked with: """ + repr(np.array([7.0, 4.0, 1.0], dtype="float32")) @@ -635,16 +635,16 @@ def test_nocopy_wrapper(): with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(int_matrix_colmajor) assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) - assert ", flags.f_contiguous" in str(excinfo.value) + assert ", \"flags.f_contiguous\"" in str(excinfo.value) assert m.get_elem_nocopy(dbl_matrix_colmajor) == 8 with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(int_matrix_rowmajor) assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) - assert ", flags.f_contiguous" in str(excinfo.value) + assert ", \"flags.f_contiguous\"" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(dbl_matrix_rowmajor) assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) - assert ", flags.f_contiguous" in str(excinfo.value) + assert ", \"flags.f_contiguous\"" in str(excinfo.value) # For the row-major test, we take a long matrix in row-major, so only the third is allowed: with pytest.raises(TypeError) as excinfo: @@ -652,20 +652,20 @@ def test_nocopy_wrapper(): assert "get_elem_rm_nocopy(): incompatible function arguments." in str( excinfo.value ) - assert ", flags.c_contiguous" in str(excinfo.value) + assert ", \"flags.c_contiguous\"" in str(excinfo.value) with pytest.raises(TypeError) as excinfo: m.get_elem_rm_nocopy(dbl_matrix_colmajor) assert "get_elem_rm_nocopy(): incompatible function arguments." in str( excinfo.value ) - assert ", flags.c_contiguous" in str(excinfo.value) + assert ", \"flags.c_contiguous\"" in str(excinfo.value) assert m.get_elem_rm_nocopy(int_matrix_rowmajor) == 8 with pytest.raises(TypeError) as excinfo: m.get_elem_rm_nocopy(dbl_matrix_rowmajor) assert "get_elem_rm_nocopy(): incompatible function arguments." in str( excinfo.value ) - assert ", flags.c_contiguous" in str(excinfo.value) + assert ", \"flags.c_contiguous\"" in str(excinfo.value) def test_eigen_ref_life_support(): @@ -701,25 +701,25 @@ def test_dense_signature(doc): assert ( doc(m.double_col) == """ - double_col(arg0: numpy.typing.NDArray[numpy.float32[m, 1]]) -> numpy.typing.NDArray[numpy.float32[m, 1]] + double_col(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[m, 1]\"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float32], \"[m, 1]\"] """ ) assert ( doc(m.double_row) == """ - double_row(arg0: numpy.typing.NDArray[numpy.float32[1, n]]) -> numpy.typing.NDArray[numpy.float32[1, n]] + double_row(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[1, n]\"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float32], \"[1, n]\"] """ ) assert doc(m.double_complex) == ( """ - double_complex(arg0: numpy.typing.NDArray[numpy.complex64[m, 1]])""" - """ -> numpy.typing.NDArray[numpy.complex64[m, 1]] + double_complex(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.complex64, \"[m, 1]\"])""" + """ -> typing.Annotated[numpy.typing.NDArray[numpy.complex64], \"[m, 1]\"] """ ) assert doc(m.double_mat_rm) == ( """ - double_mat_rm(arg0: numpy.typing.NDArray[numpy.float32[m, n]])""" - """ -> numpy.typing.NDArray[numpy.float32[m, n]] + double_mat_rm(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[m, n]\"])""" + """ -> typing.Annotated[numpy.typing.NDArray[numpy.float32], \"[m, n]\"] """ ) diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py index ea20c23327..527c45d1de 100644 --- a/tests/test_eigen_tensor.py +++ b/tests/test_eigen_tensor.py @@ -272,23 +272,23 @@ def test_round_trip_references_actually_refer(m): def test_doc_string(m, doc): assert ( doc(m.copy_tensor) - == "copy_tensor() -> numpy.typing.NDArray[numpy.float64[?, ?, ?]]" + == "copy_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[?, ?, ?]\"]" ) assert ( doc(m.copy_fixed_tensor) - == "copy_fixed_tensor() -> numpy.typing.NDArray[numpy.float64[3, 5, 2]]" + == "copy_fixed_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[3, 5, 2]\"]" ) assert ( doc(m.reference_const_tensor) - == "reference_const_tensor() -> numpy.typing.NDArray[numpy.float64[?, ?, ?]]" + == "reference_const_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[?, ?, ?]\"]" ) - order_flag = f"flags.{m.needed_options.lower()}_contiguous" + order_flag = f"\"flags.{m.needed_options.lower()}_contiguous\"" assert doc(m.round_trip_view_tensor) == ( - f"round_trip_view_tensor(arg0: numpy.typing.NDArray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])" - f" -> numpy.typing.NDArray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}]" + f"round_trip_view_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, \"[?, ?, ?]\", \"flags.writeable\", {order_flag}])" + f" -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[?, ?, ?]\", \"flags.writeable\", {order_flag}]" ) assert doc(m.round_trip_const_view_tensor) == ( - f"round_trip_const_view_tensor(arg0: numpy.typing.NDArray[numpy.float64[?, ?, ?], {order_flag}])" - " -> numpy.typing.NDArray[numpy.float64[?, ?, ?]]" + f"round_trip_const_view_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, \"[?, ?, ?]\", {order_flag}])" + " -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[?, ?, ?]\"]" ) diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index b5eef55e91..258d1ee51d 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -321,13 +321,13 @@ def test_overload_resolution(msg): msg(excinfo.value) == """ overloaded(): incompatible function arguments. The following argument types are supported: - 1. (arg0: numpy.typing.NDArray[numpy.float64]) -> str - 2. (arg0: numpy.typing.NDArray[numpy.float32]) -> str - 3. (arg0: numpy.typing.NDArray[numpy.int32]) -> str - 4. (arg0: numpy.typing.NDArray[numpy.uint16]) -> str - 5. (arg0: numpy.typing.NDArray[numpy.int64]) -> str - 6. (arg0: numpy.typing.NDArray[numpy.complex128]) -> str - 7. (arg0: numpy.typing.NDArray[numpy.complex64]) -> str + 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]) -> str + 2. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32]) -> str + 3. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.int32]) -> str + 4. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.uint16]) -> str + 5. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.int64]) -> str + 6. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.complex128]) -> str + 7. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.complex64]) -> str Invoked with: 'not an array' """ @@ -528,7 +528,7 @@ def test_index_using_ellipsis(): ], ) def test_format_descriptors_for_floating_point_types(test_func): - assert "numpy.typing.NDArray[numpy.float" in test_func.__doc__ + assert "numpy.typing.ArrayLike, numpy.float" in test_func.__doc__ @pytest.mark.parametrize("forcecast", [False, True]) diff --git a/tests/test_numpy_vectorize.py b/tests/test_numpy_vectorize.py index 55b2496b29..0768759d15 100644 --- a/tests/test_numpy_vectorize.py +++ b/tests/test_numpy_vectorize.py @@ -150,7 +150,7 @@ def test_docs(doc): assert ( doc(m.vectorized_func) == """ - vectorized_func(arg0: numpy.typing.NDArray[numpy.int32], arg1: numpy.typing.NDArray[numpy.float32], arg2: numpy.typing.NDArray[numpy.float64]) -> object + vectorized_func(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.int32], arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float32], arg2: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]) -> object """ ) @@ -212,12 +212,12 @@ def test_passthrough_arguments(doc): + ", ".join( [ "arg0: float", - "arg1: numpy.typing.NDArray[numpy.float64]", - "arg2: numpy.typing.NDArray[numpy.float64]", - "arg3: numpy.typing.NDArray[numpy.int32]", + "arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]", + "arg2: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]", + "arg3: typing.Annotated[numpy.typing.ArrayLike, numpy.int32]", "arg4: int", "arg5: m.numpy_vectorize.NonPODClass", - "arg6: numpy.typing.NDArray[numpy.float64]", + "arg6: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]", ] ) + ") -> object" From 1c0d06bff6ded7777a2f60383b017380d766b99e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 22:35:45 +0000 Subject: [PATCH 13/17] style: pre-commit fixes --- include/pybind11/eigen/matrix.h | 2 +- include/pybind11/eigen/tensor.h | 12 ++++++------ include/pybind11/numpy.h | 3 ++- tests/test_eigen_matrix.py | 22 +++++++++++----------- tests/test_eigen_tensor.py | 16 ++++++++-------- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/include/pybind11/eigen/matrix.h b/include/pybind11/eigen/matrix.h index d8a594fdee..e4ebdd012e 100644 --- a/include/pybind11/eigen/matrix.h +++ b/include/pybind11/eigen/matrix.h @@ -224,7 +224,7 @@ struct EigenProps { static constexpr bool show_f_contiguous = !show_c_contiguous && show_order && requires_col_major; - static constexpr auto descriptor + static constexpr auto descriptor = const_name("typing.Annotated[") + io_name("numpy.typing.ArrayLike, ", "numpy.typing.NDArray[") + npy_format_descriptor::name + io_name("", "]") + const_name(", \"[") diff --git a/include/pybind11/eigen/tensor.h b/include/pybind11/eigen/tensor.h index b65f29b02e..e38c12e430 100644 --- a/include/pybind11/eigen/tensor.h +++ b/include/pybind11/eigen/tensor.h @@ -126,14 +126,14 @@ struct get_tensor_descriptor { static constexpr auto details = const_name(", \"flags.writeable\"", "") + const_name < static_cast(Type::Layout) - == static_cast(Eigen::RowMajor) - > (", \"flags.c_contiguous\"", ", \"flags.f_contiguous\""); + == static_cast(Eigen::RowMajor) + > (", \"flags.c_contiguous\"", ", \"flags.f_contiguous\""); static constexpr auto value = const_name("typing.Annotated[") - + io_name("numpy.typing.ArrayLike, ", "numpy.typing.NDArray[") - + npy_format_descriptor::name + io_name("", "]") - + const_name(", \"[") + eigen_tensor_helper>::dimensions_descriptor - + const_name("]\"") + const_name(details, const_name("")) + const_name("]"); + + io_name("numpy.typing.ArrayLike, ", "numpy.typing.NDArray[") + + npy_format_descriptor::name + io_name("", "]") + + const_name(", \"[") + eigen_tensor_helper>::dimensions_descriptor + + const_name("]\"") + const_name(details, const_name("")) + const_name("]"); }; // When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes does not have the begin() member diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 8280f19873..3a370fe5a3 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -2182,7 +2182,8 @@ vectorize_helper vectorize_extractor(const Func &f, Retur template struct handle_type_name> { static constexpr auto name - = io_name("typing.Annotated[numpy.typing.ArrayLike, ", "numpy.typing.NDArray[") + npy_format_descriptor::name + const_name("]"); + = io_name("typing.Annotated[numpy.typing.ArrayLike, ", "numpy.typing.NDArray[") + + npy_format_descriptor::name + const_name("]"); }; PYBIND11_NAMESPACE_END(detail) diff --git a/tests/test_eigen_matrix.py b/tests/test_eigen_matrix.py index 47fad5d8c6..6870c29bf6 100644 --- a/tests/test_eigen_matrix.py +++ b/tests/test_eigen_matrix.py @@ -95,19 +95,19 @@ def test_mutator_descriptors(): with pytest.raises(TypeError) as excinfo: m.fixed_mutator_r(zc) assert ( - "(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[5, 6]\"," - " \"flags.writeable\", \"flags.c_contiguous\"]) -> None" in str(excinfo.value) + '(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[5, 6]",' + ' "flags.writeable", "flags.c_contiguous"]) -> None' in str(excinfo.value) ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_c(zr) assert ( - "(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[5, 6]\"," - " \"flags.writeable\", \"flags.f_contiguous\"]) -> None" in str(excinfo.value) + '(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[5, 6]",' + ' "flags.writeable", "flags.f_contiguous"]) -> None' in str(excinfo.value) ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype="float32")) assert ( - "(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[5, 6]\", \"flags.writeable\"]) -> None" + '(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[5, 6]", "flags.writeable"]) -> None' in str(excinfo.value) ) zr.flags.writeable = False @@ -635,16 +635,16 @@ def test_nocopy_wrapper(): with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(int_matrix_colmajor) assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) - assert ", \"flags.f_contiguous\"" in str(excinfo.value) + assert ', "flags.f_contiguous"' in str(excinfo.value) assert m.get_elem_nocopy(dbl_matrix_colmajor) == 8 with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(int_matrix_rowmajor) assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) - assert ", \"flags.f_contiguous\"" in str(excinfo.value) + assert ', "flags.f_contiguous"' in str(excinfo.value) with pytest.raises(TypeError) as excinfo: m.get_elem_nocopy(dbl_matrix_rowmajor) assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) - assert ", \"flags.f_contiguous\"" in str(excinfo.value) + assert ', "flags.f_contiguous"' in str(excinfo.value) # For the row-major test, we take a long matrix in row-major, so only the third is allowed: with pytest.raises(TypeError) as excinfo: @@ -652,20 +652,20 @@ def test_nocopy_wrapper(): assert "get_elem_rm_nocopy(): incompatible function arguments." in str( excinfo.value ) - assert ", \"flags.c_contiguous\"" in str(excinfo.value) + assert ', "flags.c_contiguous"' in str(excinfo.value) with pytest.raises(TypeError) as excinfo: m.get_elem_rm_nocopy(dbl_matrix_colmajor) assert "get_elem_rm_nocopy(): incompatible function arguments." in str( excinfo.value ) - assert ", \"flags.c_contiguous\"" in str(excinfo.value) + assert ', "flags.c_contiguous"' in str(excinfo.value) assert m.get_elem_rm_nocopy(int_matrix_rowmajor) == 8 with pytest.raises(TypeError) as excinfo: m.get_elem_rm_nocopy(dbl_matrix_rowmajor) assert "get_elem_rm_nocopy(): incompatible function arguments." in str( excinfo.value ) - assert ", \"flags.c_contiguous\"" in str(excinfo.value) + assert ', "flags.c_contiguous"' in str(excinfo.value) def test_eigen_ref_life_support(): diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py index 527c45d1de..67c2796103 100644 --- a/tests/test_eigen_tensor.py +++ b/tests/test_eigen_tensor.py @@ -272,23 +272,23 @@ def test_round_trip_references_actually_refer(m): def test_doc_string(m, doc): assert ( doc(m.copy_tensor) - == "copy_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[?, ?, ?]\"]" + == 'copy_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]' ) assert ( doc(m.copy_fixed_tensor) - == "copy_fixed_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[3, 5, 2]\"]" + == 'copy_fixed_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 5, 2]"]' ) assert ( doc(m.reference_const_tensor) - == "reference_const_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[?, ?, ?]\"]" + == 'reference_const_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]' ) - order_flag = f"\"flags.{m.needed_options.lower()}_contiguous\"" + order_flag = f'"flags.{m.needed_options.lower()}_contiguous"' assert doc(m.round_trip_view_tensor) == ( - f"round_trip_view_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, \"[?, ?, ?]\", \"flags.writeable\", {order_flag}])" - f" -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[?, ?, ?]\", \"flags.writeable\", {order_flag}]" + f'round_trip_view_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[?, ?, ?]", "flags.writeable", {order_flag}])' + f' -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]", "flags.writeable", {order_flag}]' ) assert doc(m.round_trip_const_view_tensor) == ( - f"round_trip_const_view_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, \"[?, ?, ?]\", {order_flag}])" - " -> typing.Annotated[numpy.typing.NDArray[numpy.float64], \"[?, ?, ?]\"]" + f'round_trip_const_view_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[?, ?, ?]", {order_flag}])' + ' -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]' ) From 702a2d9b89cf22f60bb899bc3ddd1f4ad46ffb6c Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 6 Feb 2025 18:41:49 -0500 Subject: [PATCH 14/17] re-run ci Signed-off-by: Michael Carlstrom --- tests/test_eigen_tensor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py index 67c2796103..98c4517258 100644 --- a/tests/test_eigen_tensor.py +++ b/tests/test_eigen_tensor.py @@ -280,6 +280,7 @@ def test_doc_string(m, doc): ) assert ( doc(m.reference_const_tensor) + == 'reference_const_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]' ) From 6e427363a7dddab96f25062d99f936b87aa639aa Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Thu, 6 Feb 2025 18:41:54 -0500 Subject: [PATCH 15/17] re-run ci Signed-off-by: Michael Carlstrom --- tests/test_eigen_tensor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py index 98c4517258..67c2796103 100644 --- a/tests/test_eigen_tensor.py +++ b/tests/test_eigen_tensor.py @@ -280,7 +280,6 @@ def test_doc_string(m, doc): ) assert ( doc(m.reference_const_tensor) - == 'reference_const_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]' ) From df50bc2f1e3941c9a037904737649f7b8c50bac0 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Mon, 10 Feb 2025 14:26:59 -0500 Subject: [PATCH 16/17] remove escape characters --- tests/test_eigen_matrix.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_eigen_matrix.py b/tests/test_eigen_matrix.py index 6870c29bf6..de1839d3c1 100644 --- a/tests/test_eigen_matrix.py +++ b/tests/test_eigen_matrix.py @@ -202,7 +202,7 @@ def test_negative_stride_from_python(msg): msg(excinfo.value) == """ double_threer(): incompatible function arguments. The following argument types are supported: - 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[1, 3]\", \"flags.writeable\"]) -> None + 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[1, 3]", "flags.writeable"]) -> None Invoked with: """ + repr(np.array([5.0, 4.0, 3.0], dtype="float32")) @@ -214,7 +214,7 @@ def test_negative_stride_from_python(msg): msg(excinfo.value) == """ double_threec(): incompatible function arguments. The following argument types are supported: - 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[3, 1]\", \"flags.writeable\"]) -> None + 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[3, 1]", "flags.writeable"]) -> None Invoked with: """ + repr(np.array([7.0, 4.0, 1.0], dtype="float32")) @@ -701,25 +701,25 @@ def test_dense_signature(doc): assert ( doc(m.double_col) == """ - double_col(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[m, 1]\"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float32], \"[m, 1]\"] + double_col(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[m, 1]"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float32], "[m, 1]"] """ ) assert ( doc(m.double_row) == """ - double_row(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[1, n]\"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float32], \"[1, n]\"] + double_row(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[1, n]"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float32], "[1, n]"] """ ) assert doc(m.double_complex) == ( """ - double_complex(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.complex64, \"[m, 1]\"])""" - """ -> typing.Annotated[numpy.typing.NDArray[numpy.complex64], \"[m, 1]\"] + double_complex(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.complex64, "[m, 1]"])""" + """ -> typing.Annotated[numpy.typing.NDArray[numpy.complex64], "[m, 1]"] """ ) assert doc(m.double_mat_rm) == ( """ - double_mat_rm(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, \"[m, n]\"])""" - """ -> typing.Annotated[numpy.typing.NDArray[numpy.float32], \"[m, n]\"] + double_mat_rm(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[m, n]"])""" + """ -> typing.Annotated[numpy.typing.NDArray[numpy.float32], "[m, n]"] """ ) From 0fb183c065209eecd3dfdb02122a0021e99da913 Mon Sep 17 00:00:00 2001 From: Tim Ohliger Date: Sat, 15 Feb 2025 00:10:56 +0100 Subject: [PATCH 17/17] Added tests for ArrayLike in signatures and fixed wrong types for Refs --- include/pybind11/eigen/matrix.h | 4 +++- include/pybind11/eigen/tensor.h | 5 ++++- tests/test_eigen_matrix.cpp | 4 ++++ tests/test_eigen_matrix.py | 29 ++++++++++++++++++++++++----- tests/test_eigen_tensor.py | 26 ++++++++++++++++++++++++-- tests/test_numpy_array.cpp | 9 +++++++++ tests/test_numpy_array.py | 14 ++++++++++++++ 7 files changed, 82 insertions(+), 9 deletions(-) diff --git a/include/pybind11/eigen/matrix.h b/include/pybind11/eigen/matrix.h index 79956a6f03..ca599c954c 100644 --- a/include/pybind11/eigen/matrix.h +++ b/include/pybind11/eigen/matrix.h @@ -444,7 +444,9 @@ struct eigen_map_caster { } } - static constexpr auto name = props::descriptor; + // return_descr forces the use of NDArray instead of ArrayLike in args + // since Ref<...> args can only accept arrays. + static constexpr auto name = return_descr(props::descriptor); // Explicitly delete these: support python -> C++ conversion on these (i.e. these can be return // types but not bound arguments). We still provide them (with an explicitly delete) so that diff --git a/include/pybind11/eigen/tensor.h b/include/pybind11/eigen/tensor.h index e38c12e430..50e8b50b1e 100644 --- a/include/pybind11/eigen/tensor.h +++ b/include/pybind11/eigen/tensor.h @@ -505,7 +505,10 @@ struct type_caster, std::unique_ptr value; public: - static constexpr auto name = get_tensor_descriptor::value; + // return_descr forces the use of NDArray instead of ArrayLike since refs can only reference + // arrays + static constexpr auto name + = return_descr(get_tensor_descriptor::value); explicit operator MapType *() { return value.get(); } explicit operator MapType &() { return *value; } explicit operator MapType &&() && { return std::move(*value); } diff --git a/tests/test_eigen_matrix.cpp b/tests/test_eigen_matrix.cpp index cb8e8c6259..4e6689a797 100644 --- a/tests/test_eigen_matrix.cpp +++ b/tests/test_eigen_matrix.cpp @@ -440,4 +440,8 @@ TEST_SUBMODULE(eigen_matrix, m) { py::module_::import("numpy").attr("ones")(10); return v[0](5); }); + m.def("round_trip_vector", [](const Eigen::VectorXf &x) -> Eigen::VectorXf { return x; }); + m.def("round_trip_dense", [](const DenseMatrixR &m) -> DenseMatrixR { return m; }); + m.def("round_trip_dense_ref", + [](const Eigen::Ref &m) -> Eigen::Ref { return m; }); } diff --git a/tests/test_eigen_matrix.py b/tests/test_eigen_matrix.py index de1839d3c1..9324c2a7d2 100644 --- a/tests/test_eigen_matrix.py +++ b/tests/test_eigen_matrix.py @@ -95,19 +95,19 @@ def test_mutator_descriptors(): with pytest.raises(TypeError) as excinfo: m.fixed_mutator_r(zc) assert ( - '(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[5, 6]",' + '(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[5, 6]",' ' "flags.writeable", "flags.c_contiguous"]) -> None' in str(excinfo.value) ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_c(zr) assert ( - '(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[5, 6]",' + '(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[5, 6]",' ' "flags.writeable", "flags.f_contiguous"]) -> None' in str(excinfo.value) ) with pytest.raises(TypeError) as excinfo: m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype="float32")) assert ( - '(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[5, 6]", "flags.writeable"]) -> None' + '(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[5, 6]", "flags.writeable"]) -> None' in str(excinfo.value) ) zr.flags.writeable = False @@ -202,7 +202,7 @@ def test_negative_stride_from_python(msg): msg(excinfo.value) == """ double_threer(): incompatible function arguments. The following argument types are supported: - 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[1, 3]", "flags.writeable"]) -> None + 1. (arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[1, 3]", "flags.writeable"]) -> None Invoked with: """ + repr(np.array([5.0, 4.0, 3.0], dtype="float32")) @@ -214,7 +214,7 @@ def test_negative_stride_from_python(msg): msg(excinfo.value) == """ double_threec(): incompatible function arguments. The following argument types are supported: - 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[3, 1]", "flags.writeable"]) -> None + 1. (arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[3, 1]", "flags.writeable"]) -> None Invoked with: """ + repr(np.array([7.0, 4.0, 1.0], dtype="float32")) @@ -818,3 +818,22 @@ def test_custom_operator_new(): o = m.CustomOperatorNew() np.testing.assert_allclose(o.a, 0.0) np.testing.assert_allclose(o.b.diagonal(), 1.0) + + +def test_arraylike_signature(doc): + assert doc(m.round_trip_vector) == ( + 'round_trip_vector(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[m, 1]"])' + ' -> typing.Annotated[numpy.typing.NDArray[numpy.float32], "[m, 1]"]' + ) + assert doc(m.round_trip_dense) == ( + 'round_trip_dense(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[m, n]"])' + ' -> typing.Annotated[numpy.typing.NDArray[numpy.float32], "[m, n]"]' + ) + assert doc(m.round_trip_dense_ref) == ( + 'round_trip_dense_ref(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[m, n]", "flags.writeable", "flags.c_contiguous"])' + ' -> typing.Annotated[numpy.typing.NDArray[numpy.float32], "[m, n]", "flags.writeable", "flags.c_contiguous"]' + ) + m.round_trip_vector([1.0, 2.0]) + m.round_trip_dense([[1.0, 2.0], [3.0, 4.0]]) + with pytest.raises(TypeError, match="incompatible function arguments"): + m.round_trip_dense_ref([[1.0, 2.0], [3.0, 4.0]]) diff --git a/tests/test_eigen_tensor.py b/tests/test_eigen_tensor.py index 67c2796103..4b018551bf 100644 --- a/tests/test_eigen_tensor.py +++ b/tests/test_eigen_tensor.py @@ -285,10 +285,32 @@ def test_doc_string(m, doc): order_flag = f'"flags.{m.needed_options.lower()}_contiguous"' assert doc(m.round_trip_view_tensor) == ( - f'round_trip_view_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[?, ?, ?]", "flags.writeable", {order_flag}])' + f'round_trip_view_tensor(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]", "flags.writeable", {order_flag}])' f' -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]", "flags.writeable", {order_flag}]' ) assert doc(m.round_trip_const_view_tensor) == ( - f'round_trip_const_view_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[?, ?, ?]", {order_flag}])' + f'round_trip_const_view_tensor(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]", {order_flag}])' ' -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]' ) + + +@pytest.mark.parametrize("m", submodules) +def test_arraylike_signature(m, doc): + order_flag = f'"flags.{m.needed_options.lower()}_contiguous"' + assert doc(m.round_trip_tensor) == ( + 'round_trip_tensor(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[?, ?, ?]"])' + ' -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]' + ) + assert doc(m.round_trip_tensor_noconvert) == ( + 'round_trip_tensor_noconvert(tensor: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"])' + ' -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]' + ) + assert doc(m.round_trip_view_tensor) == ( + f'round_trip_view_tensor(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]", "flags.writeable", {order_flag}])' + f' -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]", "flags.writeable", {order_flag}]' + ) + m.round_trip_tensor(tensor_ref.tolist()) + with pytest.raises(TypeError, match="incompatible function arguments"): + m.round_trip_tensor_noconvert(tensor_ref.tolist()) + with pytest.raises(TypeError, match="incompatible function arguments"): + m.round_trip_view_tensor(tensor_ref.tolist()) diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp index 79ade3ba1a..1bfca33bb6 100644 --- a/tests/test_numpy_array.cpp +++ b/tests/test_numpy_array.cpp @@ -586,4 +586,13 @@ TEST_SUBMODULE(numpy_array, sm) { sm.def("return_array_pyobject_ptr_from_list", return_array_from_list); sm.def("return_array_handle_from_list", return_array_from_list); sm.def("return_array_object_from_list", return_array_from_list); + + sm.def( + "round_trip_array_t", + [](const py::array_t &x) -> py::array_t { return x; }, + py::arg("x")); + sm.def( + "round_trip_array_t_noconvert", + [](const py::array_t &x) -> py::array_t { return x; }, + py::arg("x").noconvert()); } diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py index 258d1ee51d..3a3f22a64d 100644 --- a/tests/test_numpy_array.py +++ b/tests/test_numpy_array.py @@ -687,3 +687,17 @@ def test_return_array_object_cpp_loop(return_array, unwrap): assert isinstance(arr_from_list, np.ndarray) assert arr_from_list.dtype == np.dtype("O") assert unwrap(arr_from_list) == [6, "seven", -8.0] + + +def test_arraylike_signature(doc): + assert ( + doc(m.round_trip_array_t) + == "round_trip_array_t(x: typing.Annotated[numpy.typing.ArrayLike, numpy.float32]) -> numpy.typing.NDArray[numpy.float32]" + ) + assert ( + doc(m.round_trip_array_t_noconvert) + == "round_trip_array_t_noconvert(x: numpy.typing.NDArray[numpy.float32]) -> numpy.typing.NDArray[numpy.float32]" + ) + m.round_trip_array_t([1, 2, 3]) + with pytest.raises(TypeError, match="incompatible function arguments"): + m.round_trip_array_t_noconvert([1, 2, 3])