Skip to content

Commit dd78e78

Browse files
authored
Merge pull request #30036 from rwgk/pywrapcc_merge_sh
git merge smart_holder (after pybind/pybind11#4601 was merged)
2 parents 71598fa + 8a24bec commit dd78e78

File tree

4 files changed

+61
-7
lines changed

4 files changed

+61
-7
lines changed

include/pybind11/cast.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,14 +1236,32 @@ T cast(const handle &handle) {
12361236
}
12371237

12381238
// Note that `cast<PyObject *>(obj)` increments the reference count of `obj`.
1239-
// This is necessary for the case that `obj` is a temporary.
1239+
// This is necessary for the case that `obj` is a temporary, and could
1240+
// not possibly be different, given
1241+
// 1. the established convention that the passed `handle` is borrowed, and
1242+
// 2. we don't want to force all generic code using `cast<T>()` to special-case
1243+
// handling of `T` = `PyObject *` (to increment the reference count there).
12401244
// It is the responsibility of the caller to ensure that the reference count
12411245
// is decremented.
12421246
template <typename T,
1243-
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value, int> = 0>
1244-
T cast(const handle &handle) {
1247+
typename Handle,
1248+
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
1249+
&& detail::is_same_ignoring_cvref<Handle, handle>::value,
1250+
int>
1251+
= 0>
1252+
T cast(Handle &&handle) {
12451253
return handle.inc_ref().ptr();
12461254
}
1255+
// To optimize way an inc_ref/dec_ref cycle:
1256+
template <typename T,
1257+
typename Object,
1258+
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
1259+
&& detail::is_same_ignoring_cvref<Object, object>::value,
1260+
int>
1261+
= 0>
1262+
T cast(Object &&obj) {
1263+
return obj.release().ptr();
1264+
}
12471265

12481266
// C++ type -> py::object
12491267
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>

include/pybind11/type_caster_pyobject_ptr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
1313
template <>
1414
class type_caster<PyObject> {
1515
public:
16-
static constexpr auto name = const_name("PyObject *");
16+
static constexpr auto name = const_name("object"); // See discussion under PR #4601.
1717

1818
// This overload is purely to guard against accidents.
1919
template <typename T,

tests/test_type_caster_pyobject_ptr.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
2525
PyObject *ptr = PyLong_FromLongLong(6758L);
2626
return py::cast(ptr, py::return_value_policy::take_ownership);
2727
});
28-
m.def("cast_to_pyobject_ptr", [](py::handle obj) {
28+
m.def("cast_handle_to_pyobject_ptr", [](py::handle obj) {
2929
auto rc1 = obj.ref_count();
3030
auto *ptr = py::cast<PyObject *>(obj);
3131
auto rc2 = obj.ref_count();
@@ -34,6 +34,27 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
3434
}
3535
return 100 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>();
3636
});
37+
m.def("cast_object_to_pyobject_ptr", [](py::object obj) {
38+
py::handle hdl = obj;
39+
auto rc1 = hdl.ref_count();
40+
auto *ptr = py::cast<PyObject *>(std::move(obj));
41+
auto rc2 = hdl.ref_count();
42+
if (rc2 != rc1) {
43+
return -1;
44+
}
45+
return 300 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>();
46+
});
47+
m.def("cast_list_to_pyobject_ptr", [](py::list lst) {
48+
// This is to cover types implicitly convertible to object.
49+
py::handle hdl = lst;
50+
auto rc1 = hdl.ref_count();
51+
auto *ptr = py::cast<PyObject *>(std::move(lst));
52+
auto rc2 = hdl.ref_count();
53+
if (rc2 != rc1) {
54+
return -1;
55+
}
56+
return 400 - static_cast<int>(py::len(py::reinterpret_steal<py::list>(ptr)));
57+
});
3758

3859
m.def(
3960
"return_pyobject_ptr",
@@ -98,6 +119,8 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
98119
return i;
99120
});
100121

122+
m.def("pass_pyobject_ptr_and_int", [](PyObject *, int) {});
123+
101124
#ifdef PYBIND11_NO_COMPILE_SECTION // Change to ifndef for manual testing.
102125
{
103126
PyObject *ptr = nullptr;

tests/test_type_caster_pyobject_ptr.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,16 @@ def test_cast_from_pyobject_ptr():
1313
assert m.cast_from_pyobject_ptr() == 6758
1414

1515

16-
def test_cast_to_pyobject_ptr():
17-
assert m.cast_to_pyobject_ptr(ValueHolder(24)) == 76
16+
def test_cast_handle_to_pyobject_ptr():
17+
assert m.cast_handle_to_pyobject_ptr(ValueHolder(24)) == 76
18+
19+
20+
def test_cast_object_to_pyobject_ptr():
21+
assert m.cast_object_to_pyobject_ptr(ValueHolder(43)) == 257
22+
23+
24+
def test_cast_list_to_pyobject_ptr():
25+
assert m.cast_list_to_pyobject_ptr([1, 2, 3, 4, 5]) == 395
1826

1927

2028
def test_return_pyobject_ptr():
@@ -89,3 +97,8 @@ def test_return_list_pyobject_ptr_reference():
8997
# Insert `while True:` as the first line of this function and monitor the
9098
# process RES (Resident Memory Size) with the Unix top command.
9199
assert m.dec_ref_each_pyobject_ptr(vec_obj) == 2
100+
101+
102+
def test_type_caster_name_via_incompatible_function_arguments_type_error():
103+
with pytest.raises(TypeError, match=r"1\. \(arg0: object, arg1: int\) -> None"):
104+
m.pass_pyobject_ptr_and_int(ValueHolder(101), ValueHolder(202))

0 commit comments

Comments
 (0)