Skip to content

Commit

Permalink
Backport changes from google/pybind11clif#30021 to pybind#4601
Browse files Browse the repository at this point in the history
  • Loading branch information
Ralf W. Grosse-Kunstleve committed Apr 3, 2023
1 parent b393001 commit 0ba8003
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 23 deletions.
2 changes: 1 addition & 1 deletion include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -1062,7 +1062,7 @@ T cast(const handle &handle) {
template <typename T,
detail::enable_if_t<detail::is_same_ignoring_cvref<T, PyObject *>::value, int> = 0>
T cast(const handle &handle) {
return handle.ptr();
return handle.inc_ref().ptr();
}

// C++ type -> py::object
Expand Down
8 changes: 6 additions & 2 deletions include/pybind11/type_caster_pyobject_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ class type_caster<PyObject> {
if (policy == return_value_policy::take_ownership) {
return src;
}
Py_INCREF(src);
return src;
if (policy == return_value_policy::reference
|| policy == return_value_policy::automatic_reference) {
return handle(src).inc_ref();
}
pybind11_fail("type_caster<PyObject>::cast(): unsupported return_value_policy: "
+ std::to_string(static_cast<int>(policy)));
}

bool load(handle src, bool) {
Expand Down
18 changes: 10 additions & 8 deletions tests/test_type_caster_pyobject_ptr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) {
m.def("pass_pyobject_ptr", [](PyObject *obj) { return bool(PyTuple_CheckExact(obj)); });

m.def("call_callback_with_object_return",
[](const std::function<py::object(int mode)> &cb, int mode) { return cb(mode); });
m.def("call_callback_with_handle_return",
[](const std::function<py::handle(int mode)> &cb, int mode) { return cb(mode); });
//
m.def("call_callback_with_pyobject_ptr_return",
[](const std::function<PyObject *(int mode)> &cb, int mode) { return cb(mode); });
m.def("call_callback_with_pyobject_ptr_arg",
[](const std::function<bool(PyObject *)> &cb, py::handle obj) { return cb(obj.ptr()); });
[](const std::function<py::object(int)> &cb, int value) { return cb(value); });
m.def(
"call_callback_with_pyobject_ptr_return",
[](const std::function<PyObject *(int)> &cb, int value) { return cb(value); },
py::return_value_policy::take_ownership);
m.def(
"call_callback_with_pyobject_ptr_arg",
[](const std::function<bool(PyObject *)> &cb, py::handle obj) { return cb(obj.ptr()); },
py::arg("cb"), // This triggers return_value_policy::automatic_reference
py::arg("obj"));

m.def("cast_to_pyobject_ptr_nullptr", [](bool set_error) {
if (set_error) {
Expand Down
27 changes: 15 additions & 12 deletions tests/test_type_caster_pyobject_ptr.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,29 @@ def test_pass_pyobject_ptr():
assert not m.pass_pyobject_ptr({})


class ValueHolder:
def __init__(self, value):
self.value = value


@pytest.mark.parametrize(
"call_callback",
[
m.call_callback_with_object_return,
m.call_callback_with_handle_return,
m.call_callback_with_pyobject_ptr_return,
],
)
def test_call_callback_with_object_return(call_callback):
def cb(mode):
if mode == 0:
return 10
if mode == 1:
return "One"
raise NotImplementedError(f"Unknown mode: {mode}")

assert call_callback(cb, 0) == 10
assert call_callback(cb, 1) == "One"
with pytest.raises(NotImplementedError, match="Unknown mode: 2"):
call_callback(cb, 2)
def cb(value):
if value < 0:
raise ValueError("Raised from cb")
# Return a temporary user-defined object, to maximize sensitivity of this test.
return ValueHolder(1000 - value)

assert call_callback(cb, 287).value == 713

with pytest.raises(ValueError, match="^Raised from cb$"):
call_callback(cb, -1)


def test_call_callback_with_pyobject_ptr_arg():
Expand Down

0 comments on commit 0ba8003

Please sign in to comment.