diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index e8249956..945e9e93 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1236,14 +1236,32 @@ T cast(const handle &handle) { } // Note that `cast(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()` 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 ::value, int> = 0> -T cast(const handle &handle) { + typename Handle, + detail::enable_if_t::value + && detail::is_same_ignoring_cvref::value, + int> + = 0> +T cast(Handle &&handle) { return handle.inc_ref().ptr(); } +// To optimize way an inc_ref/dec_ref cycle: +template ::value + && detail::is_same_ignoring_cvref::value, + int> + = 0> +T cast(Object &&obj) { + return obj.release().ptr(); +} // C++ type -> py::object template ::value, int> = 0> diff --git a/include/pybind11/type_caster_pyobject_ptr.h b/include/pybind11/type_caster_pyobject_ptr.h index 77abae66..7789c040 100644 --- a/include/pybind11/type_caster_pyobject_ptr.h +++ b/include/pybind11/type_caster_pyobject_ptr.h @@ -13,7 +13,7 @@ PYBIND11_NAMESPACE_BEGIN(detail) template <> class type_caster { 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 (obj); auto rc2 = obj.ref_count(); @@ -34,6 +34,27 @@ TEST_SUBMODULE(type_caster_pyobject_ptr, m) { } return 100 - py::reinterpret_steal(ptr).attr("value").cast(); }); + m.def("cast_object_to_pyobject_ptr", [](py::object obj) { + py::handle hdl = obj; + auto rc1 = hdl.ref_count(); + auto *ptr = py::cast(std::move(obj)); + auto rc2 = hdl.ref_count(); + if (rc2 != rc1) { + return -1; + } + return 300 - py::reinterpret_steal(ptr).attr("value").cast(); + }); + 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(std::move(lst)); + auto rc2 = hdl.ref_count(); + if (rc2 != rc1) { + return -1; + } + return 400 - static_cast(py::len(py::reinterpret_steal(ptr))); + }); m.def( "return_pyobject_ptr", @@ -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; diff --git a/tests/test_type_caster_pyobject_ptr.py b/tests/test_type_caster_pyobject_ptr.py index fcb084a5..1f1ece2b 100644 --- a/tests/test_type_caster_pyobject_ptr.py +++ b/tests/test_type_caster_pyobject_ptr.py @@ -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(): @@ -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))