Skip to content

Commit

Permalink
Merge pull request #30036 from rwgk/pywrapcc_merge_sh
Browse files Browse the repository at this point in the history
git merge smart_holder (after pybind/pybind11#4601 was merged)
  • Loading branch information
Ralf W. Grosse-Kunstleve authored May 7, 2023
2 parents 71598fa + 8a24bec commit dd78e78
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 7 deletions.
24 changes: 21 additions & 3 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -1236,14 +1236,32 @@ T cast(const handle &handle) {
}

// Note that `cast<PyObject *>(obj)` increments the reference count of `obj`.
// This is necessary for the case that `obj` is a temporary.
// This is necessary for the case that `obj` is a temporary, and could
// not possibly be different, given
// 1. the established convention that the passed `handle` is borrowed, and
// 2. we don't want to force all generic code using `cast<T>()` to special-case
// handling of `T` = `PyObject *` (to increment the reference count there).
// It is the responsibility of the caller to ensure that the reference count
// is decremented.
template <typename T,
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value, int> = 0>
T cast(const handle &handle) {
typename Handle,
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
&& detail::is_same_ignoring_cvref<Handle, handle>::value,
int>
= 0>
T cast(Handle &&handle) {
return handle.inc_ref().ptr();
}
// To optimize way an inc_ref/dec_ref cycle:
template <typename T,
typename Object,
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value
&& detail::is_same_ignoring_cvref<Object, object>::value,
int>
= 0>
T cast(Object &&obj) {
return obj.release().ptr();
}

// C++ type -> py::object
template <typename T, detail::enable_if_t<!detail::is_pyobject<T>::value, int> = 0>
Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/type_caster_pyobject_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ PYBIND11_NAMESPACE_BEGIN(detail)
template <>
class type_caster<PyObject> {
public:
static constexpr auto name = const_name("PyObject *");
static constexpr auto name = const_name("object"); // See discussion under PR #4601.

// This overload is purely to guard against accidents.
template <typename T,
Expand Down
25 changes: 24 additions & 1 deletion tests/test_type_caster_pyobject_ptr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
PyObject *ptr = PyLong_FromLongLong(6758L);
return py::cast(ptr, py::return_value_policy::take_ownership);
});
m.def("cast_to_pyobject_ptr", [](py::handle obj) {
m.def("cast_handle_to_pyobject_ptr", [](py::handle obj) {
auto rc1 = obj.ref_count();
auto *ptr = py::cast<PyObject *>(obj);
auto rc2 = obj.ref_count();
Expand All @@ -34,6 +34,27 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
}
return 100 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>();
});
m.def("cast_object_to_pyobject_ptr", [](py::object obj) {
py::handle hdl = obj;
auto rc1 = hdl.ref_count();
auto *ptr = py::cast<PyObject *>(std::move(obj));
auto rc2 = hdl.ref_count();
if (rc2 != rc1) {
return -1;
}
return 300 - py::reinterpret_steal<py::object>(ptr).attr("value").cast<int>();
});
m.def("cast_list_to_pyobject_ptr", [](py::list lst) {
// This is to cover types implicitly convertible to object.
py::handle hdl = lst;
auto rc1 = hdl.ref_count();
auto *ptr = py::cast<PyObject *>(std::move(lst));
auto rc2 = hdl.ref_count();
if (rc2 != rc1) {
return -1;
}
return 400 - static_cast<int>(py::len(py::reinterpret_steal<py::list>(ptr)));
});

m.def(
"return_pyobject_ptr",
Expand Down Expand Up @@ -98,6 +119,8 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
return i;
});

m.def("pass_pyobject_ptr_and_int", [](PyObject *, int) {});

#ifdef PYBIND11_NO_COMPILE_SECTION // Change to ifndef for manual testing.
{
PyObject *ptr = nullptr;
Expand Down
17 changes: 15 additions & 2 deletions tests/test_type_caster_pyobject_ptr.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@ def test_cast_from_pyobject_ptr():
assert m.cast_from_pyobject_ptr() == 6758


def test_cast_to_pyobject_ptr():
assert m.cast_to_pyobject_ptr(ValueHolder(24)) == 76
def test_cast_handle_to_pyobject_ptr():
assert m.cast_handle_to_pyobject_ptr(ValueHolder(24)) == 76


def test_cast_object_to_pyobject_ptr():
assert m.cast_object_to_pyobject_ptr(ValueHolder(43)) == 257


def test_cast_list_to_pyobject_ptr():
assert m.cast_list_to_pyobject_ptr([1, 2, 3, 4, 5]) == 395


def test_return_pyobject_ptr():
Expand Down Expand Up @@ -89,3 +97,8 @@ def test_return_list_pyobject_ptr_reference():
# Insert `while True:` as the first line of this function and monitor the
# process RES (Resident Memory Size) with the Unix top command.
assert m.dec_ref_each_pyobject_ptr(vec_obj) == 2


def test_type_caster_name_via_incompatible_function_arguments_type_error():
with pytest.raises(TypeError, match=r"1\. \(arg0: object, arg1: int\) -> None"):
m.pass_pyobject_ptr_and_int(ValueHolder(101), ValueHolder(202))

0 comments on commit dd78e78

Please sign in to comment.