Skip to content

Commit 337dc9c

Browse files
test_numpy_dtypes: Show conversion btw cls and np.dtype / np.generic
1 parent 53d2164 commit 337dc9c

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

tests/test_numpy_dtypes.cpp

+20-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,26 @@ TEST_SUBMODULE(numpy_dtypes, m) {
259259
catch (...) { return; }
260260

261261
// typeinfo may be registered before the dtype descriptor for scalar casts to work...
262-
py::class_<SimpleStruct>(m, "SimpleStruct");
262+
py::class_<SimpleStruct>(m, "SimpleStruct")
263+
// Explicit construct with braces to ensure zero-valued initialization.
264+
.def(py::init([]() { return SimpleStruct{}; }))
265+
.def_readwrite("bool_", &SimpleStruct::bool_)
266+
.def_readwrite("uint_", &SimpleStruct::uint_)
267+
.def_readwrite("float_", &SimpleStruct::float_)
268+
.def_readwrite("ldbl_", &SimpleStruct::ldbl_)
269+
.def("astuple", [](const SimpleStruct& self) {
270+
return py::make_tuple(self.bool_, self.uint_, self.float_, self.ldbl_);
271+
})
272+
.def_static("fromtuple", [](const py::tuple tup) {
273+
if (py::len(tup) != 4) {
274+
throw py::cast_error("Invalid size");
275+
}
276+
return SimpleStruct{
277+
tup[0].cast<bool>(),
278+
tup[1].cast<uint32_t>(),
279+
tup[2].cast<float>(),
280+
tup[3].cast<long double>()};
281+
});
263282

264283
PYBIND11_NUMPY_DTYPE(SimpleStruct, bool_, uint_, float_, ldbl_);
265284
PYBIND11_NUMPY_DTYPE(SimpleStructReordered, bool_, uint_, float_, ldbl_);

tests/test_numpy_dtypes.py

+45
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ def test_recarray(simple_dtype, packed_dtype):
136136
assert_equal(arr, elements, simple_dtype)
137137
assert_equal(arr, elements, packed_dtype)
138138

139+
# Show what recarray's look like in NumPy.
140+
assert type(arr[0]) == np.void
141+
assert type(arr[0].item()) == tuple
142+
139143
if dtype == simple_dtype:
140144
assert m.print_rec_simple(arr) == [
141145
"s:0,0,0,-0",
@@ -296,6 +300,47 @@ def test_vectorize():
296300
np.testing.assert_array_equal(array, array_2)
297301

298302

303+
def test_cls_and_dtype_conversion(simple_dtype):
304+
s = m.SimpleStruct()
305+
assert s.astuple() == (False, 0, 0., 0.)
306+
assert m.SimpleStruct.fromtuple(s.astuple()).astuple() == s.astuple()
307+
308+
s.uint_ = 2
309+
assert m.f_simple(s) == 20
310+
311+
# Try as recarray of shape==(1,).
312+
s_recarray = np.array([(False, 2, 0., 0.)], dtype=simple_dtype)
313+
# Show that this will work for vectorized case.
314+
np.testing.assert_array_equal(m.f_simple_vectorized(s_recarray), [20])
315+
316+
# Show as a scalar that inherits from np.generic.
317+
s_scalar = s_recarray[0]
318+
assert isinstance(s_scalar, np.void)
319+
assert m.f_simple(s_scalar) == 20
320+
321+
# Show that an *array* scalar (np.ndarray.shape == ()) does not convert.
322+
# More specifically, conversion to SimpleStruct is not implicit.
323+
s_recarray_scalar = s_recarray.reshape(())
324+
assert isinstance(s_recarray_scalar, np.ndarray)
325+
assert s_recarray_scalar.dtype == simple_dtype
326+
with pytest.raises(TypeError) as excinfo:
327+
m.f_simple(s_recarray_scalar)
328+
assert 'incompatible function arguments' in str(excinfo.value)
329+
# Explicitly convert to m.SimpleStruct.
330+
assert m.f_simple(
331+
m.SimpleStruct.fromtuple(s_recarray_scalar.item())) == 20
332+
333+
# Show that an array of dtype=object does *not* convert.
334+
s_array_object = np.array([s])
335+
assert s_array_object.dtype == object
336+
with pytest.raises(TypeError) as excinfo:
337+
m.f_simple_vectorized(s_array_object)
338+
assert 'incompatible function arguments' in str(excinfo.value)
339+
# Explicitly convert to `np.array(..., dtype=simple_dtype)`
340+
s_array = np.array([s.astuple()], dtype=simple_dtype)
341+
np.testing.assert_array_equal(m.f_simple_vectorized(s_array), [20])
342+
343+
299344
def test_register_dtype():
300345
with pytest.raises(RuntimeError) as excinfo:
301346
m.register_dtype()

0 commit comments

Comments
 (0)