Skip to content

Commit 3d1cb7b

Browse files
committed
Merge branch 'master' into sh_merge_master
2 parents 7064d43 + 3829b76 commit 3d1cb7b

File tree

7 files changed

+144
-12
lines changed

7 files changed

+144
-12
lines changed

include/pybind11/detail/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,7 @@ constexpr const char
10041004
struct error_scope {
10051005
PyObject *type, *value, *trace;
10061006
error_scope() { PyErr_Fetch(&type, &value, &trace); }
1007+
error_scope(const error_scope &) = delete;
10071008
~error_scope() { PyErr_Restore(type, value, trace); }
10081009
};
10091010

include/pybind11/numpy.h

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -540,18 +540,16 @@ class dtype : public object {
540540
PYBIND11_OBJECT_DEFAULT(dtype, object, detail::npy_api::get().PyArrayDescr_Check_);
541541

542542
explicit dtype(const buffer_info &info) {
543-
dtype descr(_dtype_from_pep3118()(PYBIND11_STR_TYPE(info.format)));
543+
dtype descr(_dtype_from_pep3118()(pybind11::str(info.format)));
544544
// If info.itemsize == 0, use the value calculated from the format string
545545
m_ptr = descr.strip_padding(info.itemsize != 0 ? info.itemsize : descr.itemsize())
546546
.release()
547547
.ptr();
548548
}
549549

550-
explicit dtype(const std::string &format) {
551-
m_ptr = from_args(pybind11::str(format)).release().ptr();
552-
}
550+
explicit dtype(const std::string &format) : dtype(from_args(pybind11::str(format))) {}
553551

554-
explicit dtype(const char *format) : dtype(std::string(format)) {}
552+
explicit dtype(const char *format) : dtype(from_args(pybind11::str(format))) {}
555553

556554
dtype(list names, list formats, list offsets, ssize_t itemsize) {
557555
dict args;
@@ -562,6 +560,13 @@ class dtype : public object {
562560
m_ptr = from_args(std::move(args)).release().ptr();
563561
}
564562

563+
explicit dtype(int typenum)
564+
: object(detail::npy_api::get().PyArray_DescrFromType_(typenum), stolen_t{}) {
565+
if (m_ptr == nullptr) {
566+
throw error_already_set();
567+
}
568+
}
569+
565570
/// This is essentially the same as calling numpy.dtype(args) in Python.
566571
static dtype from_args(object args) {
567572
PyObject *ptr = nullptr;
@@ -596,6 +601,23 @@ class dtype : public object {
596601
return detail::array_descriptor_proxy(m_ptr)->type;
597602
}
598603

604+
/// type number of dtype.
605+
ssize_t num() const {
606+
// Note: The signature, `dtype::num` follows the naming of NumPy's public
607+
// Python API (i.e., ``dtype.num``), rather than its internal
608+
// C API (``PyArray_Descr::type_num``).
609+
return detail::array_descriptor_proxy(m_ptr)->type_num;
610+
}
611+
612+
/// Single character for byteorder
613+
char byteorder() const { return detail::array_descriptor_proxy(m_ptr)->byteorder; }
614+
615+
/// Alignment of the data type
616+
int alignment() const { return detail::array_descriptor_proxy(m_ptr)->alignment; }
617+
618+
/// Flags for the array descriptor
619+
char flags() const { return detail::array_descriptor_proxy(m_ptr)->flags; }
620+
599621
private:
600622
static object _dtype_from_pep3118() {
601623
static PyObject *obj = module_::import("numpy.core._internal")
@@ -614,7 +636,7 @@ class dtype : public object {
614636
}
615637

616638
struct field_descr {
617-
PYBIND11_STR_TYPE name;
639+
pybind11::str name;
618640
object format;
619641
pybind11::int_ offset;
620642
};
@@ -629,7 +651,7 @@ class dtype : public object {
629651
continue;
630652
}
631653
field_descriptors.push_back(
632-
{(PYBIND11_STR_TYPE) name, format.strip_padding(format.itemsize()), offset});
654+
{(pybind11::str) name, format.strip_padding(format.itemsize()), offset});
633655
}
634656

635657
std::sort(field_descriptors.begin(),
@@ -1335,7 +1357,7 @@ PYBIND11_NOINLINE void register_structured_dtype(any_container<field_descriptor>
13351357
pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ "
13361358
+ tinfo.name());
13371359
}
1338-
names.append(PYBIND11_STR_TYPE(field.name));
1360+
names.append(pybind11::str(field.name));
13391361
formats.append(field.descr);
13401362
offsets.append(pybind11::int_(field.offset));
13411363
}

include/pybind11/pytypes.h

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,7 +1588,8 @@ class capsule : public object {
15881588
}
15891589
pybind11_fail("Unable to get capsule context");
15901590
}
1591-
void *ptr = PyCapsule_GetPointer(o, nullptr);
1591+
const char *name = get_name_in_error_scope(o);
1592+
void *ptr = PyCapsule_GetPointer(o, name);
15921593
if (ptr == nullptr) {
15931594
throw error_already_set();
15941595
}
@@ -1602,7 +1603,8 @@ class capsule : public object {
16021603

16031604
explicit capsule(void (*destructor)()) {
16041605
m_ptr = PyCapsule_New(reinterpret_cast<void *>(destructor), nullptr, [](PyObject *o) {
1605-
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, nullptr));
1606+
const char *name = get_name_in_error_scope(o);
1607+
auto destructor = reinterpret_cast<void (*)()>(PyCapsule_GetPointer(o, name));
16061608
if (destructor == nullptr) {
16071609
throw error_already_set();
16081610
}
@@ -1637,7 +1639,33 @@ class capsule : public object {
16371639
}
16381640
}
16391641

1640-
const char *name() const { return PyCapsule_GetName(m_ptr); }
1642+
const char *name() const {
1643+
const char *name = PyCapsule_GetName(m_ptr);
1644+
if ((name == nullptr) && PyErr_Occurred()) {
1645+
throw error_already_set();
1646+
}
1647+
return name;
1648+
}
1649+
1650+
/// Replaces a capsule's name *without* calling the destructor on the existing one.
1651+
void set_name(const char *new_name) {
1652+
if (PyCapsule_SetName(m_ptr, new_name) != 0) {
1653+
throw error_already_set();
1654+
}
1655+
}
1656+
1657+
private:
1658+
static const char *get_name_in_error_scope(PyObject *o) {
1659+
error_scope error_guard;
1660+
1661+
const char *name = PyCapsule_GetName(o);
1662+
if ((name == nullptr) && PyErr_Occurred()) {
1663+
// write out and consume error raised by call to PyCapsule_GetName
1664+
PyErr_WriteUnraisable(o);
1665+
}
1666+
1667+
return name;
1668+
}
16411669
};
16421670

16431671
class tuple : public object {

tests/test_numpy_dtypes.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ py::list test_dtype_ctors() {
291291
list.append(py::dtype(names, formats, offsets, 20));
292292
list.append(py::dtype(py::buffer_info((void *) 0, sizeof(unsigned int), "I", 1)));
293293
list.append(py::dtype(py::buffer_info((void *) 0, 0, "T{i:a:f:b:}", 1)));
294+
list.append(py::dtype(py::detail::npy_api::NPY_DOUBLE_));
294295
return list;
295296
}
296297

@@ -440,6 +441,34 @@ TEST_SUBMODULE(numpy_dtypes, m) {
440441
}
441442
return list;
442443
});
444+
m.def("test_dtype_num", [dtype_names]() {
445+
py::list list;
446+
for (const auto &dt_name : dtype_names) {
447+
list.append(py::dtype(dt_name).num());
448+
}
449+
return list;
450+
});
451+
m.def("test_dtype_byteorder", [dtype_names]() {
452+
py::list list;
453+
for (const auto &dt_name : dtype_names) {
454+
list.append(py::dtype(dt_name).byteorder());
455+
}
456+
return list;
457+
});
458+
m.def("test_dtype_alignment", [dtype_names]() {
459+
py::list list;
460+
for (const auto &dt_name : dtype_names) {
461+
list.append(py::dtype(dt_name).alignment());
462+
}
463+
return list;
464+
});
465+
m.def("test_dtype_flags", [dtype_names]() {
466+
py::list list;
467+
for (const auto &dt_name : dtype_names) {
468+
list.append(py::dtype(dt_name).flags());
469+
}
470+
return list;
471+
});
443472
m.def("test_dtype_methods", []() {
444473
py::list list;
445474
auto dt1 = py::dtype::of<int32_t>();

tests/test_numpy_dtypes.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ def test_dtype(simple_dtype):
160160
d1,
161161
np.dtype("uint32"),
162162
d2,
163+
np.dtype("d"),
163164
]
164165

165166
assert m.test_dtype_methods() == [
@@ -175,8 +176,13 @@ def test_dtype(simple_dtype):
175176
np.zeros(1, m.trailing_padding_dtype())
176177
)
177178

179+
expected_chars = "bhilqBHILQefdgFDG?MmO"
178180
assert m.test_dtype_kind() == list("iiiiiuuuuuffffcccbMmO")
179-
assert m.test_dtype_char_() == list("bhilqBHILQefdgFDG?MmO")
181+
assert m.test_dtype_char_() == list(expected_chars)
182+
assert m.test_dtype_num() == [np.dtype(ch).num for ch in expected_chars]
183+
assert m.test_dtype_byteorder() == [np.dtype(ch).byteorder for ch in expected_chars]
184+
assert m.test_dtype_alignment() == [np.dtype(ch).alignment for ch in expected_chars]
185+
assert m.test_dtype_flags() == [chr(np.dtype(ch).flags) for ch in expected_chars]
180186

181187

182188
def test_recarray(simple_dtype, packed_dtype):

tests/test_pytypes.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,33 @@ TEST_SUBMODULE(pytypes, m) {
159159
return py::capsule([]() { py::print("destructing capsule"); });
160160
});
161161

162+
m.def("return_renamed_capsule_with_destructor", []() {
163+
py::print("creating capsule");
164+
auto cap = py::capsule([]() { py::print("destructing capsule"); });
165+
static const char *capsule_name = "test_name1";
166+
py::print("renaming capsule");
167+
cap.set_name(capsule_name);
168+
return cap;
169+
});
170+
162171
m.def("return_capsule_with_destructor_2", []() {
163172
py::print("creating capsule");
164173
return py::capsule((void *) 1234, [](void *ptr) {
165174
py::print("destructing capsule: {}"_s.format((size_t) ptr));
166175
});
167176
});
168177

178+
m.def("return_renamed_capsule_with_destructor_2", []() {
179+
py::print("creating capsule");
180+
auto cap = py::capsule((void *) 1234, [](void *ptr) {
181+
py::print("destructing capsule: {}"_s.format((size_t) ptr));
182+
});
183+
static const char *capsule_name = "test_name2";
184+
py::print("renaming capsule");
185+
cap.set_name(capsule_name);
186+
return cap;
187+
});
188+
169189
m.def("return_capsule_with_name_and_destructor", []() {
170190
auto capsule = py::capsule((void *) 12345, "pointer type description", [](PyObject *ptr) {
171191
if (ptr) {

tests/test_pytypes.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,19 @@ def test_capsule(capture):
195195
"""
196196
)
197197

198+
with capture:
199+
a = m.return_renamed_capsule_with_destructor()
200+
del a
201+
pytest.gc_collect()
202+
assert (
203+
capture.unordered
204+
== """
205+
creating capsule
206+
renaming capsule
207+
destructing capsule
208+
"""
209+
)
210+
198211
with capture:
199212
a = m.return_capsule_with_destructor_2()
200213
del a
@@ -207,6 +220,19 @@ def test_capsule(capture):
207220
"""
208221
)
209222

223+
with capture:
224+
a = m.return_renamed_capsule_with_destructor_2()
225+
del a
226+
pytest.gc_collect()
227+
assert (
228+
capture.unordered
229+
== """
230+
creating capsule
231+
renaming capsule
232+
destructing capsule: 1234
233+
"""
234+
)
235+
210236
with capture:
211237
a = m.return_capsule_with_name_and_destructor()
212238
del a

0 commit comments

Comments
 (0)