Merge branch 'master' into sh_merge_master

This commit is contained in:
Ralf W. Grosse-Kunstleve 2025-02-17 23:18:23 -08:00
commit eba3d617ee
No known key found for this signature in database
10 changed files with 142 additions and 61 deletions

View File

@ -225,19 +225,22 @@ struct EigenProps {
= !show_c_contiguous && show_order && requires_col_major; = !show_c_contiguous && show_order && requires_col_major;
static constexpr auto descriptor static constexpr auto descriptor
= const_name("numpy.ndarray[") + npy_format_descriptor<Scalar>::name + const_name("[") = const_name("typing.Annotated[")
+ io_name("numpy.typing.ArrayLike, ", "numpy.typing.NDArray[")
+ npy_format_descriptor<Scalar>::name + io_name("", "]") + const_name(", \"[")
+ const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ") + const_name<fixed_rows>(const_name<(size_t) rows>(), const_name("m")) + const_name(", ")
+ const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n")) + const_name("]") + const_name<fixed_cols>(const_name<(size_t) cols>(), const_name("n"))
+ + const_name("]\"")
// For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to // For a reference type (e.g. Ref<MatrixXd>) we have other constraints that might need to
// be satisfied: writeable=True (for a mutable reference), and, depending on the map's // 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 // 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 // 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
// an error message that you *gave* a numpy.ndarray of the right type and dimensions. // 'typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3,2]"]' and an error message
const_name<show_writeable>(", flags.writeable", "") // that you *gave* a numpy.ndarray of the right type and dimensions.
+ const_name<show_c_contiguous>(", flags.c_contiguous", "") + const_name<show_writeable>(", \"flags.writeable\"", "")
+ const_name<show_f_contiguous>(", flags.f_contiguous", "") + const_name("]"); + const_name<show_c_contiguous>(", \"flags.c_contiguous\"", "")
+ const_name<show_f_contiguous>(", \"flags.f_contiguous\"", "") + const_name("]");
}; };
// Casts an Eigen type to numpy array. If given a base, the numpy array references the src data, // Casts an Eigen type to numpy array. If given a base, the numpy array references the src data,
@ -441,7 +444,9 @@ public:
} }
} }
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 // 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 // types but not bound arguments). We still provide them (with an explicitly delete) so that

View File

@ -124,13 +124,16 @@ struct eigen_tensor_helper<
template <typename Type, bool ShowDetails, bool NeedsWriteable = false> template <typename Type, bool ShowDetails, bool NeedsWriteable = false>
struct get_tensor_descriptor { struct get_tensor_descriptor {
static constexpr auto details static constexpr auto details
= const_name<NeedsWriteable>(", flags.writeable", "") + const_name = const_name<NeedsWriteable>(", \"flags.writeable\"", "") + const_name
< static_cast<int>(Type::Layout) < static_cast<int>(Type::Layout)
== static_cast<int>(Eigen::RowMajor) > (", flags.c_contiguous", ", flags.f_contiguous"); == static_cast<int>(Eigen::RowMajor)
> (", \"flags.c_contiguous\"", ", \"flags.f_contiguous\"");
static constexpr auto value static constexpr auto value
= const_name("numpy.ndarray[") + npy_format_descriptor<typename Type::Scalar>::name = const_name("typing.Annotated[")
+ const_name("[") + eigen_tensor_helper<remove_cv_t<Type>>::dimensions_descriptor + io_name("numpy.typing.ArrayLike, ", "numpy.typing.NDArray[")
+ const_name("]") + const_name<ShowDetails>(details, const_name("")) + const_name("]"); + npy_format_descriptor<typename Type::Scalar>::name + io_name("", "]")
+ const_name(", \"[") + eigen_tensor_helper<remove_cv_t<Type>>::dimensions_descriptor
+ const_name("]\"") + const_name<ShowDetails>(details, const_name("")) + const_name("]");
}; };
// When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes<T, 0> does not have the begin() member // When EIGEN_AVOID_STL_ARRAY is defined, Eigen::DSizes<T, 0> does not have the begin() member
@ -502,7 +505,10 @@ protected:
std::unique_ptr<MapType> value; std::unique_ptr<MapType> value;
public: public:
static constexpr auto name = get_tensor_descriptor<Type, true, needs_writeable>::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<Type, true, needs_writeable>::value);
explicit operator MapType *() { return value.get(); } explicit operator MapType *() { return value.get(); }
explicit operator MapType &() { return *value; } explicit operator MapType &() { return *value; }
explicit operator MapType &&() && { return std::move(*value); } explicit operator MapType &&() && { return std::move(*value); }

View File

@ -175,7 +175,6 @@ inline numpy_internals &get_numpy_internals() {
PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) { PYBIND11_NOINLINE module_ import_numpy_core_submodule(const char *submodule_name) {
module_ numpy = module_::import("numpy"); module_ numpy = module_::import("numpy");
str version_string = numpy.attr("__version__"); 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); object numpy_version = numpy_lib.attr("NumpyVersion")(version_string);
int major_version = numpy_version.attr("major").cast<int>(); int major_version = numpy_version.attr("major").cast<int>();
@ -2183,7 +2182,8 @@ vectorize_helper<Func, Return, Args...> vectorize_extractor(const Func &f, Retur
template <typename T, int Flags> template <typename T, int Flags>
struct handle_type_name<array_t<T, Flags>> { struct handle_type_name<array_t<T, Flags>> {
static constexpr auto name static constexpr auto name
= const_name("numpy.ndarray[") + npy_format_descriptor<T>::name + const_name("]"); = io_name("typing.Annotated[numpy.typing.ArrayLike, ", "numpy.typing.NDArray[")
+ npy_format_descriptor<T>::name + const_name("]");
}; };
PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(detail)

View File

@ -440,4 +440,8 @@ TEST_SUBMODULE(eigen_matrix, m) {
py::module_::import("numpy").attr("ones")(10); py::module_::import("numpy").attr("ones")(10);
return v[0](5); 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<DenseMatrixR> &m) -> Eigen::Ref<DenseMatrixR> { return m; });
} }

View File

@ -95,19 +95,20 @@ def test_mutator_descriptors():
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.fixed_mutator_r(zc) m.fixed_mutator_r(zc)
assert ( assert (
"(arg0: numpy.ndarray[numpy.float32[5, 6]," '(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[5, 6]",'
" flags.writeable, flags.c_contiguous]) -> None" in str(excinfo.value) ' "flags.writeable", "flags.c_contiguous"]) -> None' in str(excinfo.value)
) )
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.fixed_mutator_c(zr) m.fixed_mutator_c(zr)
assert ( assert (
"(arg0: numpy.ndarray[numpy.float32[5, 6]," '(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[5, 6]",'
" flags.writeable, flags.f_contiguous]) -> None" in str(excinfo.value) ' "flags.writeable", "flags.f_contiguous"]) -> None' in str(excinfo.value)
) )
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.fixed_mutator_a(np.array([[1, 2], [3, 4]], dtype="float32")) 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 (
excinfo.value '(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[5, 6]", "flags.writeable"]) -> None'
in str(excinfo.value)
) )
zr.flags.writeable = False zr.flags.writeable = False
with pytest.raises(TypeError): with pytest.raises(TypeError):
@ -201,7 +202,7 @@ def test_negative_stride_from_python(msg):
msg(excinfo.value) msg(excinfo.value)
== """ == """
double_threer(): incompatible function arguments. The following argument types are supported: double_threer(): incompatible function arguments. The following argument types are supported:
1. (arg0: numpy.ndarray[numpy.float32[1, 3], flags.writeable]) -> None 1. (arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[1, 3]", "flags.writeable"]) -> None
Invoked with: """ Invoked with: """
+ repr(np.array([5.0, 4.0, 3.0], dtype="float32")) + repr(np.array([5.0, 4.0, 3.0], dtype="float32"))
@ -213,7 +214,7 @@ def test_negative_stride_from_python(msg):
msg(excinfo.value) msg(excinfo.value)
== """ == """
double_threec(): incompatible function arguments. The following argument types are supported: double_threec(): incompatible function arguments. The following argument types are supported:
1. (arg0: numpy.ndarray[numpy.float32[3, 1], flags.writeable]) -> None 1. (arg0: typing.Annotated[numpy.typing.NDArray[numpy.float32], "[3, 1]", "flags.writeable"]) -> None
Invoked with: """ Invoked with: """
+ repr(np.array([7.0, 4.0, 1.0], dtype="float32")) + repr(np.array([7.0, 4.0, 1.0], dtype="float32"))
@ -634,16 +635,16 @@ def test_nocopy_wrapper():
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.get_elem_nocopy(int_matrix_colmajor) m.get_elem_nocopy(int_matrix_colmajor)
assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) 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 assert m.get_elem_nocopy(dbl_matrix_colmajor) == 8
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.get_elem_nocopy(int_matrix_rowmajor) m.get_elem_nocopy(int_matrix_rowmajor)
assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) 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: with pytest.raises(TypeError) as excinfo:
m.get_elem_nocopy(dbl_matrix_rowmajor) m.get_elem_nocopy(dbl_matrix_rowmajor)
assert "get_elem_nocopy(): incompatible function arguments." in str(excinfo.value) 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: # 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: with pytest.raises(TypeError) as excinfo:
@ -651,20 +652,20 @@ def test_nocopy_wrapper():
assert "get_elem_rm_nocopy(): incompatible function arguments." in str( assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
excinfo.value excinfo.value
) )
assert ", flags.c_contiguous" in str(excinfo.value) assert ', "flags.c_contiguous"' in str(excinfo.value)
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.get_elem_rm_nocopy(dbl_matrix_colmajor) m.get_elem_rm_nocopy(dbl_matrix_colmajor)
assert "get_elem_rm_nocopy(): incompatible function arguments." in str( assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
excinfo.value 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 assert m.get_elem_rm_nocopy(int_matrix_rowmajor) == 8
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
m.get_elem_rm_nocopy(dbl_matrix_rowmajor) m.get_elem_rm_nocopy(dbl_matrix_rowmajor)
assert "get_elem_rm_nocopy(): incompatible function arguments." in str( assert "get_elem_rm_nocopy(): incompatible function arguments." in str(
excinfo.value excinfo.value
) )
assert ", flags.c_contiguous" in str(excinfo.value) assert ', "flags.c_contiguous"' in str(excinfo.value)
def test_eigen_ref_life_support(): def test_eigen_ref_life_support():
@ -700,25 +701,25 @@ def test_dense_signature(doc):
assert ( assert (
doc(m.double_col) doc(m.double_col)
== """ == """
double_col(arg0: numpy.ndarray[numpy.float32[m, 1]]) -> numpy.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 ( assert (
doc(m.double_row) doc(m.double_row)
== """ == """
double_row(arg0: numpy.ndarray[numpy.float32[1, n]]) -> numpy.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) == ( assert doc(m.double_complex) == (
""" """
double_complex(arg0: numpy.ndarray[numpy.complex64[m, 1]])""" double_complex(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.complex64, "[m, 1]"])"""
""" -> numpy.ndarray[numpy.complex64[m, 1]] """ -> typing.Annotated[numpy.typing.NDArray[numpy.complex64], "[m, 1]"]
""" """
) )
assert doc(m.double_mat_rm) == ( assert doc(m.double_mat_rm) == (
""" """
double_mat_rm(arg0: numpy.ndarray[numpy.float32[m, n]])""" double_mat_rm(arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32, "[m, n]"])"""
""" -> numpy.ndarray[numpy.float32[m, n]] """ -> typing.Annotated[numpy.typing.NDArray[numpy.float32], "[m, n]"]
""" """
) )
@ -817,3 +818,22 @@ def test_custom_operator_new():
o = m.CustomOperatorNew() o = m.CustomOperatorNew()
np.testing.assert_allclose(o.a, 0.0) np.testing.assert_allclose(o.a, 0.0)
np.testing.assert_allclose(o.b.diagonal(), 1.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]])

View File

@ -271,23 +271,46 @@ def test_round_trip_references_actually_refer(m):
@pytest.mark.parametrize("m", submodules) @pytest.mark.parametrize("m", submodules)
def test_doc_string(m, doc): def test_doc_string(m, doc):
assert ( assert (
doc(m.copy_tensor) == "copy_tensor() -> numpy.ndarray[numpy.float64[?, ?, ?]]" doc(m.copy_tensor)
== 'copy_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]"]'
) )
assert ( assert (
doc(m.copy_fixed_tensor) doc(m.copy_fixed_tensor)
== "copy_fixed_tensor() -> numpy.ndarray[numpy.float64[3, 5, 2]]" == 'copy_fixed_tensor() -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 5, 2]"]'
) )
assert ( assert (
doc(m.reference_const_tensor) doc(m.reference_const_tensor)
== "reference_const_tensor() -> numpy.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) == ( assert doc(m.round_trip_view_tensor) == (
f"round_trip_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], flags.writeable, {order_flag}])" f'round_trip_view_tensor(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]", "flags.writeable", {order_flag}])'
f" -> numpy.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) == ( assert doc(m.round_trip_const_view_tensor) == (
f"round_trip_const_view_tensor(arg0: numpy.ndarray[numpy.float64[?, ?, ?], {order_flag}])" f'round_trip_const_view_tensor(arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[?, ?, ?]", {order_flag}])'
" -> numpy.ndarray[numpy.float64[?, ?, ?]]" ' -> 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())

View File

@ -586,4 +586,13 @@ TEST_SUBMODULE(numpy_array, sm) {
sm.def("return_array_pyobject_ptr_from_list", return_array_from_list<PyObject *>); sm.def("return_array_pyobject_ptr_from_list", return_array_from_list<PyObject *>);
sm.def("return_array_handle_from_list", return_array_from_list<py::handle>); sm.def("return_array_handle_from_list", return_array_from_list<py::handle>);
sm.def("return_array_object_from_list", return_array_from_list<py::object>); sm.def("return_array_object_from_list", return_array_from_list<py::object>);
sm.def(
"round_trip_array_t",
[](const py::array_t<float> &x) -> py::array_t<float> { return x; },
py::arg("x"));
sm.def(
"round_trip_array_t_noconvert",
[](const py::array_t<float> &x) -> py::array_t<float> { return x; },
py::arg("x").noconvert());
} }

View File

@ -321,13 +321,13 @@ def test_overload_resolution(msg):
msg(excinfo.value) msg(excinfo.value)
== """ == """
overloaded(): incompatible function arguments. The following argument types are supported: overloaded(): incompatible function arguments. The following argument types are supported:
1. (arg0: numpy.ndarray[numpy.float64]) -> str 1. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]) -> str
2. (arg0: numpy.ndarray[numpy.float32]) -> str 2. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float32]) -> str
3. (arg0: numpy.ndarray[numpy.int32]) -> str 3. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.int32]) -> str
4. (arg0: numpy.ndarray[numpy.uint16]) -> str 4. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.uint16]) -> str
5. (arg0: numpy.ndarray[numpy.int64]) -> str 5. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.int64]) -> str
6. (arg0: numpy.ndarray[numpy.complex128]) -> str 6. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.complex128]) -> str
7. (arg0: numpy.ndarray[numpy.complex64]) -> str 7. (arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.complex64]) -> str
Invoked with: 'not an array' Invoked with: 'not an array'
""" """
@ -343,8 +343,8 @@ def test_overload_resolution(msg):
assert m.overloaded3(np.array([1], dtype="intc")) == "int" assert m.overloaded3(np.array([1], dtype="intc")) == "int"
expected_exc = """ expected_exc = """
overloaded3(): incompatible function arguments. The following argument types are supported: overloaded3(): incompatible function arguments. The following argument types are supported:
1. (arg0: numpy.ndarray[numpy.int32]) -> str 1. (arg0: numpy.typing.NDArray[numpy.int32]) -> str
2. (arg0: numpy.ndarray[numpy.float64]) -> str 2. (arg0: numpy.typing.NDArray[numpy.float64]) -> str
Invoked with: """ Invoked with: """
@ -528,7 +528,7 @@ def test_index_using_ellipsis():
], ],
) )
def test_format_descriptors_for_floating_point_types(test_func): def test_format_descriptors_for_floating_point_types(test_func):
assert "numpy.ndarray[numpy.float" in test_func.__doc__ assert "numpy.typing.ArrayLike, numpy.float" in test_func.__doc__
@pytest.mark.parametrize("forcecast", [False, True]) @pytest.mark.parametrize("forcecast", [False, True])
@ -687,3 +687,17 @@ def test_return_array_object_cpp_loop(return_array, unwrap):
assert isinstance(arr_from_list, np.ndarray) assert isinstance(arr_from_list, np.ndarray)
assert arr_from_list.dtype == np.dtype("O") assert arr_from_list.dtype == np.dtype("O")
assert unwrap(arr_from_list) == [6, "seven", -8.0] 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])

View File

@ -373,7 +373,7 @@ def test_complex_array():
def test_signature(doc): def test_signature(doc):
assert ( assert (
doc(m.create_rec_nested) doc(m.create_rec_nested)
== "create_rec_nested(arg0: int) -> numpy.ndarray[NestedStruct]" == "create_rec_nested(arg0: int) -> numpy.typing.NDArray[NestedStruct]"
) )

View File

@ -150,7 +150,7 @@ def test_docs(doc):
assert ( assert (
doc(m.vectorized_func) 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: 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( + ", ".join(
[ [
"arg0: float", "arg0: float",
"arg1: numpy.ndarray[numpy.float64]", "arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]",
"arg2: numpy.ndarray[numpy.float64]", "arg2: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]",
"arg3: numpy.ndarray[numpy.int32]", "arg3: typing.Annotated[numpy.typing.ArrayLike, numpy.int32]",
"arg4: int", "arg4: int",
"arg5: m.numpy_vectorize.NonPODClass", "arg5: m.numpy_vectorize.NonPODClass",
"arg6: numpy.ndarray[numpy.float64]", "arg6: typing.Annotated[numpy.typing.ArrayLike, numpy.float64]",
] ]
) )
+ ") -> object" + ") -> object"