diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e2fab9c13d..da396f0984 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -116,23 +116,6 @@ set(PYBIND11_TEST_FILES test_chrono test_class test_class_release_gil_before_calling_cpp_dtor - test_class_sh_basic - test_class_sh_disowning - test_class_sh_disowning_mi - test_class_sh_factory_constructors - test_class_sh_inheritance - test_class_sh_mi_thunks - test_class_sh_property - test_class_sh_property_non_owning - test_class_sh_shared_ptr_copy_move - test_class_sh_trampoline_basic - test_class_sh_trampoline_self_life_support - test_class_sh_trampoline_shared_from_this - test_class_sh_trampoline_shared_ptr_cpp_arg - test_class_sh_trampoline_unique_ptr - test_class_sh_unique_ptr_custom_deleter - test_class_sh_unique_ptr_member - test_class_sh_virtual_py_cpp_mix test_const_name test_constants_and_functions test_copy_move @@ -606,9 +589,6 @@ add_custom_command( ${CMAKE_CURRENT_BINARY_DIR}/sosize-$.txt) if(NOT PYBIND11_CUDA_TESTS) - # Test pure C++ code (not depending on Python). Provides the `test_pure_cpp` target. - add_subdirectory(pure_cpp) - # Test embedding the interpreter. Provides the `cpptest` target. add_subdirectory(test_embed) diff --git a/tests/test_class_sh_basic.cpp b/tests/test_class_sh_basic.cpp deleted file mode 100644 index 1d23cd62ef..0000000000 --- a/tests/test_class_sh_basic.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include -#include -#include - -namespace pybind11_tests { -namespace class_sh_basic { - -struct atyp { // Short for "any type". - std::string mtxt; - atyp() : mtxt("DefaultConstructor") {} - explicit atyp(const std::string &mtxt_) : mtxt(mtxt_) {} - atyp(const atyp &other) { mtxt = other.mtxt + "_CpCtor"; } - atyp(atyp &&other) noexcept { mtxt = other.mtxt + "_MvCtor"; } -}; - -struct uconsumer { // unique_ptr consumer - std::unique_ptr held; - bool valid() const { return static_cast(held); } - - void pass_valu(std::unique_ptr obj) { held = std::move(obj); } - void pass_rref(std::unique_ptr &&obj) { held = std::move(obj); } - std::unique_ptr rtrn_valu() { return std::move(held); } - std::unique_ptr &rtrn_lref() { return held; } - const std::unique_ptr &rtrn_cref() const { return held; } -}; - -/// Custom deleter that is default constructible. -struct custom_deleter { - std::string trace_txt; - - custom_deleter() = default; - explicit custom_deleter(const std::string &trace_txt_) : trace_txt(trace_txt_) {} - - custom_deleter(const custom_deleter &other) { trace_txt = other.trace_txt + "_CpCtor"; } - - custom_deleter &operator=(const custom_deleter &rhs) { - trace_txt = rhs.trace_txt + "_CpLhs"; - return *this; - } - - custom_deleter(custom_deleter &&other) noexcept { - trace_txt = other.trace_txt + "_MvCtorTo"; - other.trace_txt += "_MvCtorFrom"; - } - - custom_deleter &operator=(custom_deleter &&rhs) noexcept { - trace_txt = rhs.trace_txt + "_MvLhs"; - rhs.trace_txt += "_MvRhs"; - return *this; - } - - void operator()(atyp *p) const { std::default_delete()(p); } - void operator()(const atyp *p) const { std::default_delete()(p); } -}; -static_assert(std::is_default_constructible::value, ""); - -/// Custom deleter that is not default constructible. -struct custom_deleter_nd : custom_deleter { - custom_deleter_nd() = delete; - explicit custom_deleter_nd(const std::string &trace_txt_) : custom_deleter(trace_txt_) {} -}; -static_assert(!std::is_default_constructible::value, ""); - -// clang-format off - -atyp rtrn_valu() { atyp obj{"rtrn_valu"}; return obj; } -atyp&& rtrn_rref() { static atyp obj; obj.mtxt = "rtrn_rref"; return std::move(obj); } -atyp const& rtrn_cref() { static atyp obj; obj.mtxt = "rtrn_cref"; return obj; } -atyp& rtrn_mref() { static atyp obj; obj.mtxt = "rtrn_mref"; return obj; } -atyp const* rtrn_cptr() { return new atyp{"rtrn_cptr"}; } -atyp* rtrn_mptr() { return new atyp{"rtrn_mptr"}; } - -std::string pass_valu(atyp obj) { return "pass_valu:" + obj.mtxt; } // NOLINT -std::string pass_cref(atyp const& obj) { return "pass_cref:" + obj.mtxt; } -std::string pass_mref(atyp& obj) { return "pass_mref:" + obj.mtxt; } -std::string pass_cptr(atyp const* obj) { return "pass_cptr:" + obj->mtxt; } -std::string pass_mptr(atyp* obj) { return "pass_mptr:" + obj->mtxt; } - -std::shared_ptr rtrn_shmp() { return std::make_shared("rtrn_shmp"); } -std::shared_ptr rtrn_shcp() { return std::shared_ptr(new atyp{"rtrn_shcp"}); } - -std::string pass_shmp(std::shared_ptr obj) { return "pass_shmp:" + obj->mtxt; } // NOLINT -std::string pass_shcp(std::shared_ptr obj) { return "pass_shcp:" + obj->mtxt; } // NOLINT - -std::unique_ptr rtrn_uqmp() { return std::unique_ptr(new atyp{"rtrn_uqmp"}); } -std::unique_ptr rtrn_uqcp() { return std::unique_ptr(new atyp{"rtrn_uqcp"}); } - -std::string pass_uqmp(std::unique_ptr obj) { return "pass_uqmp:" + obj->mtxt; } -std::string pass_uqcp(std::unique_ptr obj) { return "pass_uqcp:" + obj->mtxt; } - -struct sddm : std::default_delete {}; -struct sddc : std::default_delete {}; - -std::unique_ptr rtrn_udmp() { return std::unique_ptr(new atyp{"rtrn_udmp"}); } -std::unique_ptr rtrn_udcp() { return std::unique_ptr(new atyp{"rtrn_udcp"}); } - -std::string pass_udmp(std::unique_ptr obj) { return "pass_udmp:" + obj->mtxt; } -std::string pass_udcp(std::unique_ptr obj) { return "pass_udcp:" + obj->mtxt; } - -std::unique_ptr rtrn_udmp_del() { return std::unique_ptr(new atyp{"rtrn_udmp_del"}, custom_deleter{"udmp_deleter"}); } -std::unique_ptr rtrn_udcp_del() { return std::unique_ptr(new atyp{"rtrn_udcp_del"}, custom_deleter{"udcp_deleter"}); } - -std::string pass_udmp_del(std::unique_ptr obj) { return "pass_udmp_del:" + obj->mtxt + "," + obj.get_deleter().trace_txt; } -std::string pass_udcp_del(std::unique_ptr obj) { return "pass_udcp_del:" + obj->mtxt + "," + obj.get_deleter().trace_txt; } - -std::unique_ptr rtrn_udmp_del_nd() { return std::unique_ptr(new atyp{"rtrn_udmp_del_nd"}, custom_deleter_nd{"udmp_deleter_nd"}); } -std::unique_ptr rtrn_udcp_del_nd() { return std::unique_ptr(new atyp{"rtrn_udcp_del_nd"}, custom_deleter_nd{"udcp_deleter_nd"}); } - -std::string pass_udmp_del_nd(std::unique_ptr obj) { return "pass_udmp_del_nd:" + obj->mtxt + "," + obj.get_deleter().trace_txt; } -std::string pass_udcp_del_nd(std::unique_ptr obj) { return "pass_udcp_del_nd:" + obj->mtxt + "," + obj.get_deleter().trace_txt; } - -// clang-format on - -// Helpers for testing. -std::string get_mtxt(atyp const &obj) { return obj.mtxt; } -std::ptrdiff_t get_ptr(atyp const &obj) { return reinterpret_cast(&obj); } - -std::unique_ptr unique_ptr_roundtrip(std::unique_ptr obj) { return obj; } - -std::string pass_unique_ptr_cref(const std::unique_ptr &obj) { return obj->mtxt; } - -const std::unique_ptr &rtrn_unique_ptr_cref(const std::string &mtxt) { - static std::unique_ptr obj{new atyp{"static_ctor_arg"}}; - if (!mtxt.empty()) { - obj->mtxt = mtxt; - } - return obj; -} - -const std::unique_ptr &unique_ptr_cref_roundtrip(const std::unique_ptr &obj) { - return obj; -} - -struct SharedPtrStash { - std::vector> stash; - void Add(const std::shared_ptr &obj) { stash.push_back(obj); } -}; - -class LocalUnusualOpRef : UnusualOpRef {}; // To avoid clashing with `py::class_`. -py::object CastUnusualOpRefConstRef(const LocalUnusualOpRef &cref) { return py::cast(cref); } -py::object CastUnusualOpRefMovable(LocalUnusualOpRef &&mvbl) { return py::cast(std::move(mvbl)); } - -} // namespace class_sh_basic -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_basic::atyp) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_basic::uconsumer) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_basic::SharedPtrStash) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_basic::LocalUnusualOpRef) - -namespace pybind11_tests { -namespace class_sh_basic { - -TEST_SUBMODULE(class_sh_basic, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - namespace py = pybind11; - - py::classh(m, "atyp").def(py::init<>()).def(py::init([](const std::string &mtxt) { - atyp obj; - obj.mtxt = mtxt; - return obj; - })); - - m.def("rtrn_valu", rtrn_valu); - m.def("rtrn_rref", rtrn_rref); - m.def("rtrn_cref", rtrn_cref); - m.def("rtrn_mref", rtrn_mref); - m.def("rtrn_cptr", rtrn_cptr); - m.def("rtrn_mptr", rtrn_mptr); - - m.def("pass_valu", pass_valu); - m.def("pass_cref", pass_cref); - m.def("pass_mref", pass_mref); - m.def("pass_cptr", pass_cptr); - m.def("pass_mptr", pass_mptr); - - m.def("rtrn_shmp", rtrn_shmp); - m.def("rtrn_shcp", rtrn_shcp); - - m.def("pass_shmp", pass_shmp); - m.def("pass_shcp", pass_shcp); - - m.def("rtrn_uqmp", rtrn_uqmp); - m.def("rtrn_uqcp", rtrn_uqcp); - - m.def("pass_uqmp", pass_uqmp); - m.def("pass_uqcp", pass_uqcp); - - m.def("rtrn_udmp", rtrn_udmp); - m.def("rtrn_udcp", rtrn_udcp); - - m.def("pass_udmp", pass_udmp); - m.def("pass_udcp", pass_udcp); - - m.def("rtrn_udmp_del", rtrn_udmp_del); - m.def("rtrn_udcp_del", rtrn_udcp_del); - - m.def("pass_udmp_del", pass_udmp_del); - m.def("pass_udcp_del", pass_udcp_del); - - m.def("rtrn_udmp_del_nd", rtrn_udmp_del_nd); - m.def("rtrn_udcp_del_nd", rtrn_udcp_del_nd); - - m.def("pass_udmp_del_nd", pass_udmp_del_nd); - m.def("pass_udcp_del_nd", pass_udcp_del_nd); - - py::classh(m, "uconsumer") - .def(py::init<>()) - .def("valid", &uconsumer::valid) - .def("pass_valu", &uconsumer::pass_valu) - .def("pass_rref", &uconsumer::pass_rref) - .def("rtrn_valu", &uconsumer::rtrn_valu) - .def("rtrn_lref", &uconsumer::rtrn_lref) - .def("rtrn_cref", &uconsumer::rtrn_cref); - - // Helpers for testing. - // These require selected functions above to work first, as indicated: - m.def("get_mtxt", get_mtxt); // pass_cref - m.def("get_ptr", get_ptr); // pass_cref - - m.def("unique_ptr_roundtrip", unique_ptr_roundtrip); // pass_uqmp, rtrn_uqmp - - m.def("pass_unique_ptr_cref", pass_unique_ptr_cref); - m.def("rtrn_unique_ptr_cref", rtrn_unique_ptr_cref); - m.def("unique_ptr_cref_roundtrip", unique_ptr_cref_roundtrip); - - py::classh(m, "SharedPtrStash") - .def(py::init<>()) - .def("Add", &SharedPtrStash::Add, py::arg("obj")); - - m.def("py_type_handle_of_atyp", []() { - return py::type::handle_of(); // Exercises static_cast in this function. - }); - - // Checks for type names used as arguments - m.def("args_shared_ptr", [](std::shared_ptr p) { return p; }); - m.def("args_shared_ptr_const", [](std::shared_ptr p) { return p; }); - m.def("args_unique_ptr", [](std::unique_ptr p) { return p; }); - m.def("args_unique_ptr_const", [](std::unique_ptr p) { return p; }); - - // Make sure unique_ptr type caster accept automatic_reference return value policy. - m.def( - "rtrn_uq_automatic_reference", - []() { return std::unique_ptr(new atyp("rtrn_uq_automatic_reference")); }, - pybind11::return_value_policy::automatic_reference); - - m.def("pass_shared_ptr_ptr", [](std::shared_ptr *) {}); - - py::classh(m, "LocalUnusualOpRef"); - m.def("CallCastUnusualOpRefConstRef", - []() { return CastUnusualOpRefConstRef(LocalUnusualOpRef()); }); - m.def("CallCastUnusualOpRefMovable", - []() { return CastUnusualOpRefMovable(LocalUnusualOpRef()); }); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} - -} // namespace class_sh_basic -} // namespace pybind11_tests diff --git a/tests/test_class_sh_basic.py b/tests/test_class_sh_basic.py deleted file mode 100644 index 74a4e40758..0000000000 --- a/tests/test_class_sh_basic.py +++ /dev/null @@ -1,249 +0,0 @@ -# Importing re before pytest after observing a PyPy CI flake when importing pytest first. -from __future__ import annotations - -import re - -import pytest - -from pybind11_tests import class_sh_basic as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_atyp_constructors(): - obj = m.atyp() - assert obj.__class__.__name__ == "atyp" - obj = m.atyp("") - assert obj.__class__.__name__ == "atyp" - obj = m.atyp("txtm") - assert obj.__class__.__name__ == "atyp" - - -@pytest.mark.parametrize( - ("rtrn_f", "expected"), - [ - (m.rtrn_valu, "rtrn_valu(_MvCtor)*_MvCtor"), - (m.rtrn_rref, "rtrn_rref(_MvCtor)*_MvCtor"), - (m.rtrn_cref, "rtrn_cref(_MvCtor)*_CpCtor"), - (m.rtrn_mref, "rtrn_mref(_MvCtor)*_CpCtor"), - (m.rtrn_cptr, "rtrn_cptr"), - (m.rtrn_mptr, "rtrn_mptr"), - (m.rtrn_shmp, "rtrn_shmp"), - (m.rtrn_shcp, "rtrn_shcp"), - (m.rtrn_uqmp, "rtrn_uqmp"), - (m.rtrn_uqcp, "rtrn_uqcp"), - (m.rtrn_udmp, "rtrn_udmp"), - (m.rtrn_udcp, "rtrn_udcp"), - ], -) -def test_cast(rtrn_f, expected): - assert re.match(expected, m.get_mtxt(rtrn_f())) - - -@pytest.mark.parametrize( - ("pass_f", "mtxt", "expected"), - [ - (m.pass_valu, "Valu", "pass_valu:Valu(_MvCtor)*_CpCtor"), - (m.pass_cref, "Cref", "pass_cref:Cref(_MvCtor)*_MvCtor"), - (m.pass_mref, "Mref", "pass_mref:Mref(_MvCtor)*_MvCtor"), - (m.pass_cptr, "Cptr", "pass_cptr:Cptr(_MvCtor)*_MvCtor"), - (m.pass_mptr, "Mptr", "pass_mptr:Mptr(_MvCtor)*_MvCtor"), - (m.pass_shmp, "Shmp", "pass_shmp:Shmp(_MvCtor)*_MvCtor"), - (m.pass_shcp, "Shcp", "pass_shcp:Shcp(_MvCtor)*_MvCtor"), - (m.pass_uqmp, "Uqmp", "pass_uqmp:Uqmp(_MvCtor)*_MvCtor"), - (m.pass_uqcp, "Uqcp", "pass_uqcp:Uqcp(_MvCtor)*_MvCtor"), - ], -) -def test_load_with_mtxt(pass_f, mtxt, expected): - assert re.match(expected, pass_f(m.atyp(mtxt))) - - -@pytest.mark.parametrize( - ("pass_f", "rtrn_f", "expected"), - [ - (m.pass_udmp, m.rtrn_udmp, "pass_udmp:rtrn_udmp"), - (m.pass_udcp, m.rtrn_udcp, "pass_udcp:rtrn_udcp"), - ], -) -def test_load_with_rtrn_f(pass_f, rtrn_f, expected): - assert pass_f(rtrn_f()) == expected - - -@pytest.mark.parametrize( - ("pass_f", "rtrn_f", "regex_expected"), - [ - ( - m.pass_udmp_del, - m.rtrn_udmp_del, - "pass_udmp_del:rtrn_udmp_del,udmp_deleter(_MvCtorTo)*_MvCtorTo", - ), - ( - m.pass_udcp_del, - m.rtrn_udcp_del, - "pass_udcp_del:rtrn_udcp_del,udcp_deleter(_MvCtorTo)*_MvCtorTo", - ), - ( - m.pass_udmp_del_nd, - m.rtrn_udmp_del_nd, - "pass_udmp_del_nd:rtrn_udmp_del_nd,udmp_deleter_nd(_MvCtorTo)*_MvCtorTo", - ), - ( - m.pass_udcp_del_nd, - m.rtrn_udcp_del_nd, - "pass_udcp_del_nd:rtrn_udcp_del_nd,udcp_deleter_nd(_MvCtorTo)*_MvCtorTo", - ), - ], -) -def test_deleter_roundtrip(pass_f, rtrn_f, regex_expected): - assert re.match(regex_expected, pass_f(rtrn_f())) - - -@pytest.mark.parametrize( - ("pass_f", "rtrn_f", "expected"), - [ - (m.pass_uqmp, m.rtrn_uqmp, "pass_uqmp:rtrn_uqmp"), - (m.pass_uqcp, m.rtrn_uqcp, "pass_uqcp:rtrn_uqcp"), - (m.pass_udmp, m.rtrn_udmp, "pass_udmp:rtrn_udmp"), - (m.pass_udcp, m.rtrn_udcp, "pass_udcp:rtrn_udcp"), - ], -) -def test_pass_unique_ptr_disowns(pass_f, rtrn_f, expected): - obj = rtrn_f() - assert pass_f(obj) == expected - with pytest.raises(ValueError) as exc_info: - pass_f(obj) - assert str(exc_info.value) == ( - "Missing value for wrapped C++ type" - + " `pybind11_tests::class_sh_basic::atyp`:" - + " Python instance was disowned." - ) - - -@pytest.mark.parametrize( - ("pass_f", "rtrn_f"), - [ - (m.pass_uqmp, m.rtrn_uqmp), - (m.pass_uqcp, m.rtrn_uqcp), - (m.pass_udmp, m.rtrn_udmp), - (m.pass_udcp, m.rtrn_udcp), - ], -) -def test_cannot_disown_use_count_ne_1(pass_f, rtrn_f): - obj = rtrn_f() - stash = m.SharedPtrStash() - stash.Add(obj) - with pytest.raises(ValueError) as exc_info: - pass_f(obj) - assert str(exc_info.value) == ("Cannot disown use_count != 1 (load_as_unique_ptr).") - - -def test_unique_ptr_roundtrip(num_round_trips=1000): - # Multiple roundtrips to stress-test instance registration/deregistration. - recycled = m.atyp("passenger") - for _ in range(num_round_trips): - id_orig = id(recycled) - recycled = m.unique_ptr_roundtrip(recycled) - assert re.match("passenger(_MvCtor)*_MvCtor", m.get_mtxt(recycled)) - id_rtrn = id(recycled) - # Ensure the returned object is a different Python instance. - assert id_rtrn != id_orig - id_orig = id_rtrn - - -def test_pass_unique_ptr_cref(): - obj = m.atyp("ctor_arg") - assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.get_mtxt(obj)) - assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.pass_unique_ptr_cref(obj)) - assert re.match("ctor_arg(_MvCtor)*_MvCtor", m.get_mtxt(obj)) - - -def test_rtrn_unique_ptr_cref(): - obj0 = m.rtrn_unique_ptr_cref("") - assert m.get_mtxt(obj0) == "static_ctor_arg" - obj1 = m.rtrn_unique_ptr_cref("passed_mtxt_1") - assert m.get_mtxt(obj1) == "passed_mtxt_1" - assert m.get_mtxt(obj0) == "passed_mtxt_1" - assert obj0 is obj1 - - -def test_unique_ptr_cref_roundtrip(num_round_trips=1000): - # Multiple roundtrips to stress-test implementation. - orig = m.atyp("passenger") - mtxt_orig = m.get_mtxt(orig) - recycled = orig - for _ in range(num_round_trips): - recycled = m.unique_ptr_cref_roundtrip(recycled) - assert recycled is orig - assert m.get_mtxt(recycled) == mtxt_orig - - -@pytest.mark.parametrize( - ("pass_f", "rtrn_f", "moved_out", "moved_in"), - [ - (m.uconsumer.pass_valu, m.uconsumer.rtrn_valu, True, True), - (m.uconsumer.pass_rref, m.uconsumer.rtrn_valu, True, True), - (m.uconsumer.pass_valu, m.uconsumer.rtrn_lref, True, False), - (m.uconsumer.pass_valu, m.uconsumer.rtrn_cref, True, False), - ], -) -def test_unique_ptr_consumer_roundtrip(pass_f, rtrn_f, moved_out, moved_in): - c = m.uconsumer() - assert not c.valid() - recycled = m.atyp("passenger") - mtxt_orig = m.get_mtxt(recycled) - assert re.match("passenger_(MvCtor){1,2}", mtxt_orig) - - pass_f(c, recycled) - if moved_out: - with pytest.raises(ValueError) as excinfo: - m.get_mtxt(recycled) - assert "Python instance was disowned" in str(excinfo.value) - - recycled = rtrn_f(c) - assert c.valid() != moved_in - assert m.get_mtxt(recycled) == mtxt_orig - - -def test_py_type_handle_of_atyp(): - obj = m.py_type_handle_of_atyp() - assert obj.__class__.__name__ == "pybind11_type" - - -def test_function_signatures(doc): - assert ( - doc(m.args_shared_ptr) - == "args_shared_ptr(arg0: m.class_sh_basic.atyp) -> m.class_sh_basic.atyp" - ) - assert ( - doc(m.args_shared_ptr_const) - == "args_shared_ptr_const(arg0: m.class_sh_basic.atyp) -> m.class_sh_basic.atyp" - ) - assert ( - doc(m.args_unique_ptr) - == "args_unique_ptr(arg0: m.class_sh_basic.atyp) -> m.class_sh_basic.atyp" - ) - assert ( - doc(m.args_unique_ptr_const) - == "args_unique_ptr_const(arg0: m.class_sh_basic.atyp) -> m.class_sh_basic.atyp" - ) - - -def test_unique_ptr_return_value_policy_automatic_reference(): - assert m.get_mtxt(m.rtrn_uq_automatic_reference()) == "rtrn_uq_automatic_reference" - - -def test_pass_shared_ptr_ptr(): - obj = m.atyp() - with pytest.raises(RuntimeError) as excinfo: - m.pass_shared_ptr_ptr(obj) - assert str(excinfo.value) == ( - "Passing `std::shared_ptr *` from Python to C++ is not supported" - " (inherently unsafe)." - ) - - -def test_unusual_op_ref(): - # Merely to test that this still exists and built successfully. - assert m.CallCastUnusualOpRefConstRef().__class__.__name__ == "LocalUnusualOpRef" - assert m.CallCastUnusualOpRefMovable().__class__.__name__ == "LocalUnusualOpRef" diff --git a/tests/test_class_sh_disowning.cpp b/tests/test_class_sh_disowning.cpp deleted file mode 100644 index 81e17ac437..0000000000 --- a/tests/test_class_sh_disowning.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_disowning { - -template // Using int as a trick to easily generate a series of types. -struct Atype { - int val = 0; - explicit Atype(int val_) : val{val_} {} - int get() const { return val * 10 + SerNo; } -}; - -int same_twice(std::unique_ptr> at1a, std::unique_ptr> at1b) { - return at1a->get() * 100 + at1b->get() * 10; -} - -int mixed(std::unique_ptr> at1, std::unique_ptr> at2) { - return at1->get() * 200 + at2->get() * 20; -} - -int overloaded(std::unique_ptr> at1, int i) { return at1->get() * 30 + i; } -int overloaded(std::unique_ptr> at2, int i) { return at2->get() * 40 + i; } - -} // namespace class_sh_disowning -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning::Atype<1>) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning::Atype<2>) - -TEST_SUBMODULE(class_sh_disowning, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - using namespace pybind11_tests::class_sh_disowning; - - py::classh>(m, "Atype1").def(py::init()).def("get", &Atype<1>::get); - py::classh>(m, "Atype2").def(py::init()).def("get", &Atype<2>::get); - - m.def("same_twice", same_twice); - - m.def("mixed", mixed); - - m.def("overloaded", (int (*)(std::unique_ptr>, int)) &overloaded); - m.def("overloaded", (int (*)(std::unique_ptr>, int)) &overloaded); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_disowning.py b/tests/test_class_sh_disowning.py deleted file mode 100644 index 95fd30c556..0000000000 --- a/tests/test_class_sh_disowning.py +++ /dev/null @@ -1,81 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_disowning as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def is_disowned(obj): - try: - obj.get() - except ValueError: - return True - return False - - -def test_same_twice(): - while True: - obj1a = m.Atype1(57) - obj1b = m.Atype1(62) - assert m.same_twice(obj1a, obj1b) == (57 * 10 + 1) * 100 + (62 * 10 + 1) * 10 - assert is_disowned(obj1a) - assert is_disowned(obj1b) - obj1c = m.Atype1(0) - with pytest.raises(ValueError): - # Disowning works for one argument, but not both. - m.same_twice(obj1c, obj1c) - assert is_disowned(obj1c) - return # Comment out for manual leak checking (use `top` command). - - -def test_mixed(): - first_pass = True - while True: - obj1a = m.Atype1(90) - obj2a = m.Atype2(25) - assert m.mixed(obj1a, obj2a) == (90 * 10 + 1) * 200 + (25 * 10 + 2) * 20 - assert is_disowned(obj1a) - assert is_disowned(obj2a) - - # The C++ order of evaluation of function arguments is (unfortunately) unspecified: - # https://en.cppreference.com/w/cpp/language/eval_order - # Read on. - obj1b = m.Atype1(0) - with pytest.raises(ValueError): - # If the 1st argument is evaluated first, obj1b is disowned before the conversion for - # the already disowned obj2a fails as expected. - m.mixed(obj1b, obj2a) - obj2b = m.Atype2(0) - with pytest.raises(ValueError): - # If the 2nd argument is evaluated first, obj2b is disowned before the conversion for - # the already disowned obj1a fails as expected. - m.mixed(obj1a, obj2b) - - # Either obj1b or obj2b was disowned in the expected failed m.mixed() calls above, but not - # both. - is_disowned_results = (is_disowned(obj1b), is_disowned(obj2b)) - assert is_disowned_results.count(True) == 1 - if first_pass: - first_pass = False - ix = is_disowned_results.index(True) + 1 - print(f"\nC++ function argument {ix} is evaluated first.") - - return # Comment out for manual leak checking (use `top` command). - - -def test_overloaded(): - while True: - obj1 = m.Atype1(81) - obj2 = m.Atype2(60) - with pytest.raises(TypeError): - m.overloaded(obj1, "NotInt") - assert obj1.get() == 81 * 10 + 1 # Not disowned. - assert m.overloaded(obj1, 3) == (81 * 10 + 1) * 30 + 3 - with pytest.raises(TypeError): - m.overloaded(obj2, "NotInt") - assert obj2.get() == 60 * 10 + 2 # Not disowned. - assert m.overloaded(obj2, 2) == (60 * 10 + 2) * 40 + 2 - return # Comment out for manual leak checking (use `top` command). diff --git a/tests/test_class_sh_disowning_mi.cpp b/tests/test_class_sh_disowning_mi.cpp deleted file mode 100644 index 6622927583..0000000000 --- a/tests/test_class_sh_disowning_mi.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_disowning_mi { - -// Diamond inheritance (copied from test_multiple_inheritance.cpp). -struct B { - int val_b = 10; - B() = default; - B(const B &) = default; - virtual ~B() = default; -}; - -struct C0 : public virtual B { - int val_c0 = 20; -}; - -struct C1 : public virtual B { - int val_c1 = 21; -}; - -struct D : public C0, public C1 { - int val_d = 30; -}; - -void disown_b(std::unique_ptr) {} - -// test_multiple_inheritance_python -struct Base1 { - explicit Base1(int i) : i(i) {} - int foo() const { return i; } - int i; -}; - -struct Base2 { - explicit Base2(int j) : j(j) {} - int bar() const { return j; } - int j; -}; - -int disown_base1(std::unique_ptr b1) { return b1->i * 2000 + 1; } -int disown_base2(std::unique_ptr b2) { return b2->j * 2000 + 2; } - -} // namespace class_sh_disowning_mi -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::B) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::C0) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::C1) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::D) - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::Base1) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_disowning_mi::Base2) - -TEST_SUBMODULE(class_sh_disowning_mi, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - using namespace pybind11_tests::class_sh_disowning_mi; - - py::classh(m, "B") - .def(py::init<>()) - .def_readonly("val_b", &D::val_b) - .def("b", [](B *self) { return self; }) - .def("get", [](const B &self) { return self.val_b; }); - - py::classh(m, "C0") - .def(py::init<>()) - .def_readonly("val_c0", &D::val_c0) - .def("c0", [](C0 *self) { return self; }) - .def("get", [](const C0 &self) { return self.val_b * 100 + self.val_c0; }); - - py::classh(m, "C1") - .def(py::init<>()) - .def_readonly("val_c1", &D::val_c1) - .def("c1", [](C1 *self) { return self; }) - .def("get", [](const C1 &self) { return self.val_b * 100 + self.val_c1; }); - - py::classh(m, "D") - .def(py::init<>()) - .def_readonly("val_d", &D::val_d) - .def("d", [](D *self) { return self; }) - .def("get", [](const D &self) { - return self.val_b * 1000000 + self.val_c0 * 10000 + self.val_c1 * 100 + self.val_d; - }); - - m.def("disown_b", disown_b); - - // test_multiple_inheritance_python - py::classh(m, "Base1").def(py::init()).def("foo", &Base1::foo); - py::classh(m, "Base2").def(py::init()).def("bar", &Base2::bar); - m.def("disown_base1", disown_base1); - m.def("disown_base2", disown_base2); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_disowning_mi.py b/tests/test_class_sh_disowning_mi.py deleted file mode 100644 index 7b5261242b..0000000000 --- a/tests/test_class_sh_disowning_mi.py +++ /dev/null @@ -1,249 +0,0 @@ -from __future__ import annotations - -import pytest - -import env # noqa: F401 -from pybind11_tests import class_sh_disowning_mi as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_diamond_inheritance(): - # Very similar to test_multiple_inheritance.py:test_diamond_inheritance. - d = m.D() - assert d is d.d() - assert d is d.c0() - assert d is d.c1() - assert d is d.b() - assert d is d.c0().b() - assert d is d.c1().b() - assert d is d.c0().c1().b().c0().b() - - -def is_disowned(callable_method): - try: - callable_method() - except ValueError as e: - assert "Python instance was disowned" in str(e) # noqa: PT017 - return True - return False - - -def test_disown_b(): - b = m.B() - assert b.get() == 10 - m.disown_b(b) - assert is_disowned(b.get) - - -@pytest.mark.parametrize("var_to_disown", ["c0", "b"]) -def test_disown_c0(var_to_disown): - c0 = m.C0() - assert c0.get() == 1020 - b = c0.b() - m.disown_b(locals()[var_to_disown]) - assert is_disowned(c0.get) - assert is_disowned(b.get) - - -@pytest.mark.parametrize("var_to_disown", ["c1", "b"]) -def test_disown_c1(var_to_disown): - c1 = m.C1() - assert c1.get() == 1021 - b = c1.b() - m.disown_b(locals()[var_to_disown]) - assert is_disowned(c1.get) - assert is_disowned(b.get) - - -@pytest.mark.parametrize("var_to_disown", ["d", "c1", "c0", "b"]) -def test_disown_d(var_to_disown): - d = m.D() - assert d.get() == 10202130 - b = d.b() - c0 = d.c0() - c1 = d.c1() - m.disown_b(locals()[var_to_disown]) - assert is_disowned(d.get) - assert is_disowned(c1.get) - assert is_disowned(c0.get) - assert is_disowned(b.get) - - -# Based on test_multiple_inheritance.py:test_multiple_inheritance_python. -class MI1(m.Base1, m.Base2): - def __init__(self, i, j): - m.Base1.__init__(self, i) - m.Base2.__init__(self, j) - - -class B1: - def v(self): - return 1 - - -class MI2(B1, m.Base1, m.Base2): - def __init__(self, i, j): - B1.__init__(self) - m.Base1.__init__(self, i) - m.Base2.__init__(self, j) - - -class MI3(MI2): - def __init__(self, i, j): - MI2.__init__(self, i, j) - - -class MI4(MI3, m.Base2): - def __init__(self, i, j): - MI3.__init__(self, i, j) - # This should be ignored (Base2 is already initialized via MI2): - m.Base2.__init__(self, i + 100) - - -class MI5(m.Base2, B1, m.Base1): - def __init__(self, i, j): - B1.__init__(self) - m.Base1.__init__(self, i) - m.Base2.__init__(self, j) - - -class MI6(m.Base2, B1): - def __init__(self, i): - m.Base2.__init__(self, i) - B1.__init__(self) - - -class B2(B1): - def v(self): - return 2 - - -class B3: - def v(self): - return 3 - - -class B4(B3, B2): - def v(self): - return 4 - - -class MI7(B4, MI6): - def __init__(self, i): - B4.__init__(self) - MI6.__init__(self, i) - - -class MI8(MI6, B3): - def __init__(self, i): - MI6.__init__(self, i) - B3.__init__(self) - - -class MI8b(B3, MI6): - def __init__(self, i): - B3.__init__(self) - MI6.__init__(self, i) - - -@pytest.mark.xfail("env.PYPY") -def test_multiple_inheritance_python(): - # Based on test_multiple_inheritance.py:test_multiple_inheritance_python. - # Exercises values_and_holders with 2 value_and_holder instances. - - mi1 = MI1(1, 2) - assert mi1.foo() == 1 - assert mi1.bar() == 2 - - mi2 = MI2(3, 4) - assert mi2.v() == 1 - assert mi2.foo() == 3 - assert mi2.bar() == 4 - - mi3 = MI3(5, 6) - assert mi3.v() == 1 - assert mi3.foo() == 5 - assert mi3.bar() == 6 - - mi4 = MI4(7, 8) - assert mi4.v() == 1 - assert mi4.foo() == 7 - assert mi4.bar() == 8 - - mi5 = MI5(10, 11) - assert mi5.v() == 1 - assert mi5.foo() == 10 - assert mi5.bar() == 11 - - mi6 = MI6(12) - assert mi6.v() == 1 - assert mi6.bar() == 12 - - mi7 = MI7(13) - assert mi7.v() == 4 - assert mi7.bar() == 13 - - mi8 = MI8(14) - assert mi8.v() == 1 - assert mi8.bar() == 14 - - mi8b = MI8b(15) - assert mi8b.v() == 3 - assert mi8b.bar() == 15 - - -DISOWN_CLS_I_J_V_LIST = [ - (MI1, 1, 2, None), - (MI2, 3, 4, 1), - (MI3, 5, 6, 1), - (MI4, 7, 8, 1), - (MI5, 10, 11, 1), -] - - -@pytest.mark.xfail("env.PYPY", strict=False) -@pytest.mark.parametrize(("cls", "i", "j", "v"), DISOWN_CLS_I_J_V_LIST) -def test_disown_base1_first(cls, i, j, v): - obj = cls(i, j) - assert obj.foo() == i - assert m.disown_base1(obj) == 2000 * i + 1 - assert is_disowned(obj.foo) - assert obj.bar() == j - assert m.disown_base2(obj) == 2000 * j + 2 - assert is_disowned(obj.bar) - if v is not None: - assert obj.v() == v - - -@pytest.mark.xfail("env.PYPY", strict=False) -@pytest.mark.parametrize(("cls", "i", "j", "v"), DISOWN_CLS_I_J_V_LIST) -def test_disown_base2_first(cls, i, j, v): - obj = cls(i, j) - assert obj.bar() == j - assert m.disown_base2(obj) == 2000 * j + 2 - assert is_disowned(obj.bar) - assert obj.foo() == i - assert m.disown_base1(obj) == 2000 * i + 1 - assert is_disowned(obj.foo) - if v is not None: - assert obj.v() == v - - -@pytest.mark.xfail("env.PYPY", strict=False) -@pytest.mark.parametrize( - ("cls", "j", "v"), - [ - (MI6, 12, 1), - (MI7, 13, 4), - (MI8, 14, 1), - (MI8b, 15, 3), - ], -) -def test_disown_base2(cls, j, v): - obj = cls(j) - assert obj.bar() == j - assert m.disown_base2(obj) == 2000 * j + 2 - assert is_disowned(obj.bar) - assert obj.v() == v diff --git a/tests/test_class_sh_factory_constructors.cpp b/tests/test_class_sh_factory_constructors.cpp deleted file mode 100644 index f4c0c1d5c2..0000000000 --- a/tests/test_class_sh_factory_constructors.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include -#include - -namespace pybind11_tests { -namespace class_sh_factory_constructors { - -template // Using int as a trick to easily generate a series of types. -struct atyp { // Short for "any type". - std::string mtxt; -}; - -template -std::string get_mtxt(const T &obj) { - return obj.mtxt; -} - -using atyp_valu = atyp<0x0>; -using atyp_rref = atyp<0x1>; -using atyp_cref = atyp<0x2>; -using atyp_mref = atyp<0x3>; -using atyp_cptr = atyp<0x4>; -using atyp_mptr = atyp<0x5>; -using atyp_shmp = atyp<0x6>; -using atyp_shcp = atyp<0x7>; -using atyp_uqmp = atyp<0x8>; -using atyp_uqcp = atyp<0x9>; -using atyp_udmp = atyp<0xA>; -using atyp_udcp = atyp<0xB>; - -// clang-format off - -atyp_valu rtrn_valu() { atyp_valu obj{"Valu"}; return obj; } -atyp_rref&& rtrn_rref() { static atyp_rref obj; obj.mtxt = "Rref"; return std::move(obj); } -atyp_cref const& rtrn_cref() { static atyp_cref obj; obj.mtxt = "Cref"; return obj; } -atyp_mref& rtrn_mref() { static atyp_mref obj; obj.mtxt = "Mref"; return obj; } -atyp_cptr const* rtrn_cptr() { return new atyp_cptr{"Cptr"}; } -atyp_mptr* rtrn_mptr() { return new atyp_mptr{"Mptr"}; } - -std::shared_ptr rtrn_shmp() { return std::make_shared(atyp_shmp{"Shmp"}); } -std::shared_ptr rtrn_shcp() { return std::shared_ptr(new atyp_shcp{"Shcp"}); } - -std::unique_ptr rtrn_uqmp() { return std::unique_ptr(new atyp_uqmp{"Uqmp"}); } -std::unique_ptr rtrn_uqcp() { return std::unique_ptr(new atyp_uqcp{"Uqcp"}); } - -struct sddm : std::default_delete {}; -struct sddc : std::default_delete {}; - -std::unique_ptr rtrn_udmp() { return std::unique_ptr(new atyp_udmp{"Udmp"}); } -std::unique_ptr rtrn_udcp() { return std::unique_ptr(new atyp_udcp{"Udcp"}); } - -// clang-format on - -// Minimalistic approach to achieve full coverage of construct() overloads for constructing -// smart_holder from unique_ptr and shared_ptr returns. -struct with_alias { - int val = 0; - virtual ~with_alias() = default; - // Some compilers complain about implicitly defined versions of some of the following: - with_alias() = default; - with_alias(const with_alias &) = default; - with_alias(with_alias &&) = default; - with_alias &operator=(const with_alias &) = default; - with_alias &operator=(with_alias &&) = default; -}; -struct with_alias_alias : with_alias {}; -struct sddwaa : std::default_delete {}; - -} // namespace class_sh_factory_constructors -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_valu) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_rref) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_cref) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_mref) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_cptr) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_mptr) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_shmp) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_shcp) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_uqmp) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_uqcp) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_udmp) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::atyp_udcp) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_factory_constructors::with_alias) - -TEST_SUBMODULE(class_sh_factory_constructors, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - using namespace pybind11_tests::class_sh_factory_constructors; - - py::classh(m, "atyp_valu") - .def(py::init(&rtrn_valu)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_rref") - .def(py::init(&rtrn_rref)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_cref") - // class_: ... must return a compatible ... - // classh: ... cannot pass object of non-trivial type ... - // .def(py::init(&rtrn_cref)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_mref") - // class_: ... must return a compatible ... - // classh: ... cannot pass object of non-trivial type ... - // .def(py::init(&rtrn_mref)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_cptr") - // class_: ... must return a compatible ... - // classh: ... must return a compatible ... - // .def(py::init(&rtrn_cptr)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_mptr") - .def(py::init(&rtrn_mptr)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_shmp") - .def(py::init(&rtrn_shmp)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_shcp") - // py::class_>(m, "atyp_shcp") - // class_: ... must return a compatible ... - // classh: ... cannot pass object of non-trivial type ... - // .def(py::init(&rtrn_shcp)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_uqmp") - .def(py::init(&rtrn_uqmp)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_uqcp") - // class_: ... cannot pass object of non-trivial type ... - // classh: ... cannot pass object of non-trivial type ... - // .def(py::init(&rtrn_uqcp)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_udmp") - .def(py::init(&rtrn_udmp)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "atyp_udcp") - // py::class_>(m, "atyp_udcp") - // class_: ... must return a compatible ... - // classh: ... cannot pass object of non-trivial type ... - // .def(py::init(&rtrn_udcp)) - .def("get_mtxt", get_mtxt); - - py::classh(m, "with_alias") - .def_readonly("val", &with_alias::val) - .def(py::init([](int i) { - auto p = std::unique_ptr(new with_alias_alias); - p->val = i * 100; - return p; - })) - .def(py::init([](int i, int j) { - auto p = std::unique_ptr(new with_alias_alias); - p->val = i * 100 + j * 10; - return p; - })) - .def(py::init([](int i, int j, int k) { - auto p = std::make_shared(); - p->val = i * 100 + j * 10 + k; - return p; - })) - .def(py::init( - [](int, int, int, int) { return std::unique_ptr(new with_alias); }, - [](int, int, int, int) { - return std::unique_ptr(new with_alias); // Invalid alias factory. - })) - .def(py::init([](int, int, int, int, int) { return std::make_shared(); }, - [](int, int, int, int, int) { - return std::make_shared(); // Invalid alias factory. - })); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_factory_constructors.py b/tests/test_class_sh_factory_constructors.py deleted file mode 100644 index 149a33401e..0000000000 --- a/tests/test_class_sh_factory_constructors.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_factory_constructors as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_atyp_factories(): - assert m.atyp_valu().get_mtxt() == "Valu" - assert m.atyp_rref().get_mtxt() == "Rref" - # sert m.atyp_cref().get_mtxt() == "Cref" - # sert m.atyp_mref().get_mtxt() == "Mref" - # sert m.atyp_cptr().get_mtxt() == "Cptr" - assert m.atyp_mptr().get_mtxt() == "Mptr" - assert m.atyp_shmp().get_mtxt() == "Shmp" - # sert m.atyp_shcp().get_mtxt() == "Shcp" - assert m.atyp_uqmp().get_mtxt() == "Uqmp" - # sert m.atyp_uqcp().get_mtxt() == "Uqcp" - assert m.atyp_udmp().get_mtxt() == "Udmp" - # sert m.atyp_udcp().get_mtxt() == "Udcp" - - -@pytest.mark.parametrize( - ("init_args", "expected"), - [ - ((3,), 300), - ((5, 7), 570), - ((9, 11, 13), 1023), - ], -) -def test_with_alias_success(init_args, expected): - assert m.with_alias(*init_args).val == expected - - -@pytest.mark.parametrize( - ("num_init_args", "smart_ptr"), - [ - (4, "std::unique_ptr"), - (5, "std::shared_ptr"), - ], -) -def test_with_alias_invalid(num_init_args, smart_ptr): - class PyDrvdWithAlias(m.with_alias): - pass - - with pytest.raises(TypeError) as excinfo: - PyDrvdWithAlias(*((0,) * num_init_args)) - assert ( - str(excinfo.value) - == "pybind11::init(): construction failed: returned " - + smart_ptr - + " pointee is not an alias instance" - ) diff --git a/tests/test_class_sh_inheritance.cpp b/tests/test_class_sh_inheritance.cpp deleted file mode 100644 index 86ecea9be7..0000000000 --- a/tests/test_class_sh_inheritance.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_inheritance { - -template -struct base_template { - base_template() : base_id(Id) {} - virtual ~base_template() = default; - virtual int id() const { return base_id; } - int base_id; - - // Some compilers complain about implicitly defined versions of some of the following: - base_template(const base_template &) = default; - base_template(base_template &&) noexcept = default; - base_template &operator=(const base_template &) = default; - base_template &operator=(base_template &&) noexcept = default; -}; - -using base = base_template<100>; - -struct drvd : base { - int id() const override { return 2 * base_id; } -}; - -// clang-format off -inline drvd *rtrn_mptr_drvd() { return new drvd; } -inline base *rtrn_mptr_drvd_up_cast() { return new drvd; } - -inline int pass_cptr_base(base const *b) { return b->id() + 11; } -inline int pass_cptr_drvd(drvd const *d) { return d->id() + 12; } - -inline std::shared_ptr rtrn_shmp_drvd() { return std::make_shared(); } -inline std::shared_ptr rtrn_shmp_drvd_up_cast() { return std::make_shared(); } - -inline int pass_shcp_base(const std::shared_ptr& b) { return b->id() + 21; } -inline int pass_shcp_drvd(const std::shared_ptr& d) { return d->id() + 22; } -// clang-format on - -using base1 = base_template<110>; -using base2 = base_template<120>; - -// Not reusing base here because it would interfere with the single-inheritance test. -struct drvd2 : base1, base2 { - int id() const override { return 3 * base1::base_id + 4 * base2::base_id; } -}; - -// clang-format off -inline drvd2 *rtrn_mptr_drvd2() { return new drvd2; } -inline base1 *rtrn_mptr_drvd2_up_cast1() { return new drvd2; } -inline base2 *rtrn_mptr_drvd2_up_cast2() { return new drvd2; } - -inline int pass_cptr_base1(base1 const *b) { return b->id() + 21; } -inline int pass_cptr_base2(base2 const *b) { return b->id() + 22; } -inline int pass_cptr_drvd2(drvd2 const *d) { return d->id() + 23; } -// clang-format on - -} // namespace class_sh_inheritance -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_inheritance::base) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_inheritance::drvd) - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_inheritance::base1) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_inheritance::base2) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_inheritance::drvd2) - -namespace pybind11_tests { -namespace class_sh_inheritance { - -TEST_SUBMODULE(class_sh_inheritance, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - py::classh(m, "base"); - py::classh(m, "drvd"); - - auto rvto = py::return_value_policy::take_ownership; - - m.def("rtrn_mptr_drvd", rtrn_mptr_drvd, rvto); - m.def("rtrn_mptr_drvd_up_cast", rtrn_mptr_drvd_up_cast, rvto); - m.def("pass_cptr_base", pass_cptr_base); - m.def("pass_cptr_drvd", pass_cptr_drvd); - - m.def("rtrn_shmp_drvd", rtrn_shmp_drvd); - m.def("rtrn_shmp_drvd_up_cast", rtrn_shmp_drvd_up_cast); - m.def("pass_shcp_base", pass_shcp_base); - m.def("pass_shcp_drvd", pass_shcp_drvd); - - // __init__ needed for Python inheritance. - py::classh(m, "base1").def(py::init<>()); - py::classh(m, "base2").def(py::init<>()); - py::classh(m, "drvd2"); - - m.def("rtrn_mptr_drvd2", rtrn_mptr_drvd2, rvto); - m.def("rtrn_mptr_drvd2_up_cast1", rtrn_mptr_drvd2_up_cast1, rvto); - m.def("rtrn_mptr_drvd2_up_cast2", rtrn_mptr_drvd2_up_cast2, rvto); - m.def("pass_cptr_base1", pass_cptr_base1); - m.def("pass_cptr_base2", pass_cptr_base2); - m.def("pass_cptr_drvd2", pass_cptr_drvd2); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} - -} // namespace class_sh_inheritance -} // namespace pybind11_tests diff --git a/tests/test_class_sh_inheritance.py b/tests/test_class_sh_inheritance.py deleted file mode 100644 index 4f8c0e6982..0000000000 --- a/tests/test_class_sh_inheritance.py +++ /dev/null @@ -1,68 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_inheritance as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_rtrn_mptr_drvd_pass_cptr_base(): - d = m.rtrn_mptr_drvd() - i = m.pass_cptr_base(d) # load_impl Case 2a - assert i == 2 * 100 + 11 - - -def test_rtrn_shmp_drvd_pass_shcp_base(): - d = m.rtrn_shmp_drvd() - i = m.pass_shcp_base(d) # load_impl Case 2a - assert i == 2 * 100 + 21 - - -def test_rtrn_mptr_drvd_up_cast_pass_cptr_drvd(): - b = m.rtrn_mptr_drvd_up_cast() - # the base return is down-cast immediately. - assert b.__class__.__name__ == "drvd" - i = m.pass_cptr_drvd(b) - assert i == 2 * 100 + 12 - - -def test_rtrn_shmp_drvd_up_cast_pass_shcp_drvd(): - b = m.rtrn_shmp_drvd_up_cast() - # the base return is down-cast immediately. - assert b.__class__.__name__ == "drvd" - i = m.pass_shcp_drvd(b) - assert i == 2 * 100 + 22 - - -def test_rtrn_mptr_drvd2_pass_cptr_bases(): - d = m.rtrn_mptr_drvd2() - i1 = m.pass_cptr_base1(d) # load_impl Case 2c - assert i1 == 3 * 110 + 4 * 120 + 21 - i2 = m.pass_cptr_base2(d) - assert i2 == 3 * 110 + 4 * 120 + 22 - - -def test_rtrn_mptr_drvd2_up_casts_pass_cptr_drvd2(): - b1 = m.rtrn_mptr_drvd2_up_cast1() - assert b1.__class__.__name__ == "drvd2" - i1 = m.pass_cptr_drvd2(b1) - assert i1 == 3 * 110 + 4 * 120 + 23 - b2 = m.rtrn_mptr_drvd2_up_cast2() - assert b2.__class__.__name__ == "drvd2" - i2 = m.pass_cptr_drvd2(b2) - assert i2 == 3 * 110 + 4 * 120 + 23 - - -def test_python_drvd2(): - class Drvd2(m.base1, m.base2): - def __init__(self): - m.base1.__init__(self) - m.base2.__init__(self) - - d = Drvd2() - i1 = m.pass_cptr_base1(d) # load_impl Case 2b - assert i1 == 110 + 21 - i2 = m.pass_cptr_base2(d) - assert i2 == 120 + 22 diff --git a/tests/test_class_sh_mi_thunks.cpp b/tests/test_class_sh_mi_thunks.cpp deleted file mode 100644 index 2630ab0dc8..0000000000 --- a/tests/test_class_sh_mi_thunks.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include - -#include "pybind11_tests.h" - -#include -#include -#include - -namespace test_class_sh_mi_thunks { - -// For general background: https://shaharmike.com/cpp/vtable-part2/ -// C++ vtables - Part 2 - Multiple Inheritance -// ... the compiler creates a 'thunk' method that corrects `this` ... - -struct Base0 { - virtual ~Base0() = default; - Base0() = default; - Base0(const Base0 &) = delete; -}; - -struct Base1 { - virtual ~Base1() = default; - // Using `vector` here because it is known to make this test very sensitive to bugs. - std::vector vec = {1, 2, 3, 4, 5}; - Base1() = default; - Base1(const Base1 &) = delete; -}; - -struct Derived : Base1, Base0 { - ~Derived() override = default; - Derived() = default; - Derived(const Derived &) = delete; -}; - -} // namespace test_class_sh_mi_thunks - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_mi_thunks::Base0) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_mi_thunks::Base1) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_mi_thunks::Derived) - -TEST_SUBMODULE(class_sh_mi_thunks, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - using namespace test_class_sh_mi_thunks; - - m.def("ptrdiff_drvd_base0", []() { - auto drvd = std::unique_ptr(new Derived); - auto *base0 = dynamic_cast(drvd.get()); - return std::ptrdiff_t(reinterpret_cast(drvd.get()) - - reinterpret_cast(base0)); - }); - - py::classh(m, "Base0"); - py::classh(m, "Base1"); - py::classh(m, "Derived"); - - m.def( - "get_drvd_as_base0_raw_ptr", - []() { - auto *drvd = new Derived; - auto *base0 = dynamic_cast(drvd); - return base0; - }, - py::return_value_policy::take_ownership); - - m.def("get_drvd_as_base0_shared_ptr", []() { - auto drvd = std::make_shared(); - auto base0 = std::dynamic_pointer_cast(drvd); - return base0; - }); - - m.def("get_drvd_as_base0_unique_ptr", []() { - auto drvd = std::unique_ptr(new Derived); - auto base0 = std::unique_ptr(std::move(drvd)); - return base0; - }); - - m.def("vec_size_base0_raw_ptr", [](const Base0 *obj) { - const auto *obj_der = dynamic_cast(obj); - if (obj_der == nullptr) { - return std::size_t(0); - } - return obj_der->vec.size(); - }); - - m.def("vec_size_base0_shared_ptr", [](const std::shared_ptr &obj) -> std::size_t { - const auto obj_der = std::dynamic_pointer_cast(obj); - if (!obj_der) { - return std::size_t(0); - } - return obj_der->vec.size(); - }); - - m.def("vec_size_base0_unique_ptr", [](std::unique_ptr obj) -> std::size_t { - const auto *obj_der = dynamic_cast(obj.get()); - if (obj_der == nullptr) { - return std::size_t(0); - } - return obj_der->vec.size(); - }); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_mi_thunks.py b/tests/test_class_sh_mi_thunks.py deleted file mode 100644 index af3971bba4..0000000000 --- a/tests/test_class_sh_mi_thunks.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_mi_thunks as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_ptrdiff_drvd_base0(): - ptrdiff = m.ptrdiff_drvd_base0() - # A failure here does not (necessarily) mean that there is a bug, but that - # test_class_sh_mi_thunks is not exercising what it is supposed to. - # If this ever fails on some platforms: use pytest.skip() - # If this ever fails on all platforms: don't know, seems extremely unlikely. - assert ptrdiff != 0 - - -@pytest.mark.parametrize( - "vec_size_fn", - [ - m.vec_size_base0_raw_ptr, - m.vec_size_base0_shared_ptr, - ], -) -@pytest.mark.parametrize( - "get_fn", - [ - m.get_drvd_as_base0_raw_ptr, - m.get_drvd_as_base0_shared_ptr, - m.get_drvd_as_base0_unique_ptr, - ], -) -def test_get_vec_size_raw_shared(get_fn, vec_size_fn): - obj = get_fn() - assert vec_size_fn(obj) == 5 - - -@pytest.mark.parametrize( - "get_fn", [m.get_drvd_as_base0_raw_ptr, m.get_drvd_as_base0_unique_ptr] -) -def test_get_vec_size_unique(get_fn): - obj = get_fn() - assert m.vec_size_base0_unique_ptr(obj) == 5 - with pytest.raises(ValueError, match="Python instance was disowned"): - m.vec_size_base0_unique_ptr(obj) - - -def test_get_shared_vec_size_unique(): - obj = m.get_drvd_as_base0_shared_ptr() - with pytest.raises(ValueError) as exc_info: - m.vec_size_base0_unique_ptr(obj) - assert ( - str(exc_info.value) == "Cannot disown external shared_ptr (load_as_unique_ptr)." - ) diff --git a/tests/test_class_sh_property.cpp b/tests/test_class_sh_property.cpp deleted file mode 100644 index 22a1f69502..0000000000 --- a/tests/test_class_sh_property.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// The compact 4-character naming matches that in test_class_sh_basic.cpp -// Variable names are intentionally terse, to not distract from the more important C++ type names: -// valu(e), ref(erence), ptr or p (pointer), r = rvalue, m = mutable, c = const, -// sh = shared_ptr, uq = unique_ptr. - -#include "pybind11/smart_holder.h" -#include "pybind11_tests.h" - -#include - -namespace test_class_sh_property { - -struct ClassicField { - int num = -88; -}; - -struct ClassicOuter { - ClassicField *m_mptr = nullptr; - const ClassicField *m_cptr = nullptr; -}; - -struct Field { - int num = -99; -}; - -struct Outer { - Field m_valu; - Field *m_mptr = nullptr; - const Field *m_cptr = nullptr; - std::unique_ptr m_uqmp; - std::unique_ptr m_uqcp; - std::shared_ptr m_shmp; - std::shared_ptr m_shcp; -}; - -inline void DisownOuter(std::unique_ptr) {} - -struct WithCharArrayMember { - WithCharArrayMember() { std::memcpy(char6_member, "Char6", 6); } - char char6_member[6]; -}; - -struct WithConstCharPtrMember { - const char *const_char_ptr_member = "ConstChar*"; -}; - -} // namespace test_class_sh_property - -PYBIND11_TYPE_CASTER_BASE_HOLDER(test_class_sh_property::ClassicField, - std::unique_ptr) -PYBIND11_TYPE_CASTER_BASE_HOLDER(test_class_sh_property::ClassicOuter, - std::unique_ptr) - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::Field) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::Outer) - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::WithCharArrayMember) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(test_class_sh_property::WithConstCharPtrMember) - -TEST_SUBMODULE(class_sh_property, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - using namespace test_class_sh_property; - - py::class_>(m, "ClassicField") - .def(py::init<>()) - .def_readwrite("num", &ClassicField::num); - - py::class_>(m, "ClassicOuter") - .def(py::init<>()) - .def_readonly("m_mptr_readonly", &ClassicOuter::m_mptr) - .def_readwrite("m_mptr_readwrite", &ClassicOuter::m_mptr) - .def_readwrite("m_cptr_readonly", &ClassicOuter::m_cptr) - .def_readwrite("m_cptr_readwrite", &ClassicOuter::m_cptr); - - py::classh(m, "Field").def(py::init<>()).def_readwrite("num", &Field::num); - - py::classh(m, "Outer") - .def(py::init<>()) - - .def_readonly("m_valu_readonly", &Outer::m_valu) - .def_readwrite("m_valu_readwrite", &Outer::m_valu) - - .def_readonly("m_mptr_readonly", &Outer::m_mptr) - .def_readwrite("m_mptr_readwrite", &Outer::m_mptr) - .def_readonly("m_cptr_readonly", &Outer::m_cptr) - .def_readwrite("m_cptr_readwrite", &Outer::m_cptr) - - // .def_readonly("m_uqmp_readonly", &Outer::m_uqmp) // Custom compilation Error. - .def_readwrite("m_uqmp_readwrite", &Outer::m_uqmp) - // .def_readonly("m_uqcp_readonly", &Outer::m_uqcp) // Custom compilation Error. - .def_readwrite("m_uqcp_readwrite", &Outer::m_uqcp) - - .def_readwrite("m_shmp_readonly", &Outer::m_shmp) - .def_readwrite("m_shmp_readwrite", &Outer::m_shmp) - .def_readwrite("m_shcp_readonly", &Outer::m_shcp) - .def_readwrite("m_shcp_readwrite", &Outer::m_shcp); - - m.def("DisownOuter", DisownOuter); - - py::classh(m, "WithCharArrayMember") - .def(py::init<>()) - .def_readonly("char6_member", &WithCharArrayMember::char6_member); - - py::classh(m, "WithConstCharPtrMember") - .def(py::init<>()) - .def_readonly("const_char_ptr_member", &WithConstCharPtrMember::const_char_ptr_member); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_property.py b/tests/test_class_sh_property.py deleted file mode 100644 index 9bd2fbfbf8..0000000000 --- a/tests/test_class_sh_property.py +++ /dev/null @@ -1,169 +0,0 @@ -# The compact 4-character naming scheme (e.g. mptr, cptr, shcp) is explained at the top of -# test_class_sh_property.cpp. -from __future__ import annotations - -import pytest - -import env # noqa: F401 -from pybind11_tests import class_sh_property as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -@pytest.mark.skipif( - "env.PYPY or env.GRAALPY", reason="gc after `del field` is apparently deferred" -) -@pytest.mark.parametrize("m_attr", ["m_valu_readonly", "m_valu_readwrite"]) -def test_valu_getter(m_attr): - # Reduced from PyCLIF test: - # https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/testing/python/nested_fields_test.py#L56 - outer = m.Outer() - field = getattr(outer, m_attr) - assert field.num == -99 - with pytest.raises(ValueError) as excinfo: - m.DisownOuter(outer) - assert str(excinfo.value) == "Cannot disown use_count != 1 (load_as_unique_ptr)." - del field - m.DisownOuter(outer) - with pytest.raises(ValueError, match="Python instance was disowned") as excinfo: - getattr(outer, m_attr) - - -def test_valu_setter(): - outer = m.Outer() - assert outer.m_valu_readonly.num == -99 - assert outer.m_valu_readwrite.num == -99 - field = m.Field() - field.num = 35 - outer.m_valu_readwrite = field - assert outer.m_valu_readonly.num == 35 - assert outer.m_valu_readwrite.num == 35 - - -@pytest.mark.parametrize("m_attr", ["m_shmp", "m_shcp"]) -def test_shp(m_attr): - m_attr_readonly = m_attr + "_readonly" - m_attr_readwrite = m_attr + "_readwrite" - outer = m.Outer() - assert getattr(outer, m_attr_readonly) is None - assert getattr(outer, m_attr_readwrite) is None - field = m.Field() - field.num = 43 - setattr(outer, m_attr_readwrite, field) - assert getattr(outer, m_attr_readonly).num == 43 - assert getattr(outer, m_attr_readwrite).num == 43 - getattr(outer, m_attr_readonly).num = 57 - getattr(outer, m_attr_readwrite).num = 57 - assert field.num == 57 - del field - assert getattr(outer, m_attr_readonly).num == 57 - assert getattr(outer, m_attr_readwrite).num == 57 - - -@pytest.mark.parametrize( - ("field_type", "num_default", "outer_type"), - [ - (m.ClassicField, -88, m.ClassicOuter), - (m.Field, -99, m.Outer), - ], -) -@pytest.mark.parametrize("m_attr", ["m_mptr", "m_cptr"]) -@pytest.mark.parametrize("r_kind", ["_readonly", "_readwrite"]) -def test_ptr(field_type, num_default, outer_type, m_attr, r_kind): - m_attr_r_kind = m_attr + r_kind - outer = outer_type() - assert getattr(outer, m_attr_r_kind) is None - field = field_type() - assert field.num == num_default - setattr(outer, m_attr + "_readwrite", field) - assert getattr(outer, m_attr_r_kind).num == num_default - field.num = 76 - assert getattr(outer, m_attr_r_kind).num == 76 - # Change to -88 or -99 to demonstrate Undefined Behavior (dangling pointer). - if num_default == 88 and m_attr == "m_mptr": - del field - assert getattr(outer, m_attr_r_kind).num == 76 - - -@pytest.mark.parametrize("m_attr_readwrite", ["m_uqmp_readwrite", "m_uqcp_readwrite"]) -def test_uqp(m_attr_readwrite): - outer = m.Outer() - assert getattr(outer, m_attr_readwrite) is None - field_orig = m.Field() - field_orig.num = 39 - setattr(outer, m_attr_readwrite, field_orig) - with pytest.raises(ValueError, match="Python instance was disowned"): - _ = field_orig.num - field_retr1 = getattr(outer, m_attr_readwrite) - assert getattr(outer, m_attr_readwrite) is None - assert field_retr1.num == 39 - field_retr1.num = 93 - setattr(outer, m_attr_readwrite, field_retr1) - with pytest.raises(ValueError): - _ = field_retr1.num - field_retr2 = getattr(outer, m_attr_readwrite) - assert field_retr2.num == 93 - - -# Proof-of-concept (POC) for safe & intuitive Python access to unique_ptr members. -# The C++ member unique_ptr is disowned to a temporary Python object for accessing -# an attribute of the member. After the attribute was accessed, the Python object -# is disowned back to the C++ member unique_ptr. -# Productizing this POC is left for a future separate PR, as needed. -class unique_ptr_field_proxy_poc: - def __init__(self, obj, field_name): - object.__setattr__(self, "__obj", obj) - object.__setattr__(self, "__field_name", field_name) - - def __getattr__(self, *args, **kwargs): - return _proxy_dereference(self, getattr, *args, **kwargs) - - def __setattr__(self, *args, **kwargs): - return _proxy_dereference(self, setattr, *args, **kwargs) - - def __delattr__(self, *args, **kwargs): - return _proxy_dereference(self, delattr, *args, **kwargs) - - -def _proxy_dereference(proxy, xxxattr, *args, **kwargs): - obj = object.__getattribute__(proxy, "__obj") - field_name = object.__getattribute__(proxy, "__field_name") - field = getattr(obj, field_name) # Disowns the C++ unique_ptr member. - assert field is not None - try: - return xxxattr(field, *args, **kwargs) - finally: - setattr(obj, field_name, field) # Disowns the temporary Python object (field). - - -@pytest.mark.parametrize("m_attr", ["m_uqmp", "m_uqcp"]) -def test_unique_ptr_field_proxy_poc(m_attr): - m_attr_readwrite = m_attr + "_readwrite" - outer = m.Outer() - field_orig = m.Field() - field_orig.num = 45 - setattr(outer, m_attr_readwrite, field_orig) - field_proxy = unique_ptr_field_proxy_poc(outer, m_attr_readwrite) - assert field_proxy.num == 45 - assert field_proxy.num == 45 - with pytest.raises(AttributeError): - _ = field_proxy.xyz - assert field_proxy.num == 45 - field_proxy.num = 82 - assert field_proxy.num == 82 - field_proxy = unique_ptr_field_proxy_poc(outer, m_attr_readwrite) - assert field_proxy.num == 82 - with pytest.raises(AttributeError): - del field_proxy.num - assert field_proxy.num == 82 - - -def test_readonly_char6_member(): - obj = m.WithCharArrayMember() - assert obj.char6_member == "Char6" - - -def test_readonly_const_char_ptr_member(): - obj = m.WithConstCharPtrMember() - assert obj.const_char_ptr_member == "ConstChar*" diff --git a/tests/test_class_sh_property_non_owning.cpp b/tests/test_class_sh_property_non_owning.cpp deleted file mode 100644 index 87b84334fc..0000000000 --- a/tests/test_class_sh_property_non_owning.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "pybind11/smart_holder.h" -#include "pybind11_tests.h" - -#include -#include - -namespace test_class_sh_property_non_owning { - -struct CoreField { - explicit CoreField(int int_value = -99) : int_value{int_value} {} - int int_value; -}; - -struct DataField { - DataField(int i_value, int i_shared, int i_unique) - : core_fld_value{i_value}, core_fld_shared_ptr{new CoreField{i_shared}}, - core_fld_raw_ptr{core_fld_shared_ptr.get()}, - core_fld_unique_ptr{new CoreField{i_unique}} {} - CoreField core_fld_value; - std::shared_ptr core_fld_shared_ptr; - CoreField *core_fld_raw_ptr; - std::unique_ptr core_fld_unique_ptr; -}; - -struct DataFieldsHolder { -private: - std::vector vec; - -public: - explicit DataFieldsHolder(std::size_t vec_size) { - for (std::size_t i = 0; i < vec_size; i++) { - int i11 = static_cast(i) * 11; - vec.emplace_back(13 + i11, 14 + i11, 15 + i11); - } - } - - DataField *vec_at(std::size_t index) { - if (index >= vec.size()) { - return nullptr; - } - return &vec[index]; - } -}; - -} // namespace test_class_sh_property_non_owning - -using namespace test_class_sh_property_non_owning; - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(CoreField) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataField) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(DataFieldsHolder) - -TEST_SUBMODULE(class_sh_property_non_owning, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - py::classh(m, "CoreField").def_readwrite("int_value", &CoreField::int_value); - - py::classh(m, "DataField") - .def_readonly("core_fld_value_ro", &DataField::core_fld_value) - .def_readwrite("core_fld_value_rw", &DataField::core_fld_value) - .def_readonly("core_fld_shared_ptr_ro", &DataField::core_fld_shared_ptr) - .def_readwrite("core_fld_shared_ptr_rw", &DataField::core_fld_shared_ptr) - .def_readonly("core_fld_raw_ptr_ro", &DataField::core_fld_raw_ptr) - .def_readwrite("core_fld_raw_ptr_rw", &DataField::core_fld_raw_ptr) - .def_readwrite("core_fld_unique_ptr_rw", &DataField::core_fld_unique_ptr); - - py::classh(m, "DataFieldsHolder") - .def(py::init()) - .def("vec_at", &DataFieldsHolder::vec_at, py::return_value_policy::reference_internal); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_property_non_owning.py b/tests/test_class_sh_property_non_owning.py deleted file mode 100644 index b977e75b37..0000000000 --- a/tests/test_class_sh_property_non_owning.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_property_non_owning as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -@pytest.mark.parametrize("persistent_holder", [True, False]) -@pytest.mark.parametrize( - ("core_fld", "expected"), - [ - ("core_fld_value_ro", (13, 24)), - ("core_fld_value_rw", (13, 24)), - ("core_fld_shared_ptr_ro", (14, 25)), - ("core_fld_shared_ptr_rw", (14, 25)), - ("core_fld_raw_ptr_ro", (14, 25)), - ("core_fld_raw_ptr_rw", (14, 25)), - ("core_fld_unique_ptr_rw", (15, 26)), - ], -) -def test_core_fld_common(core_fld, expected, persistent_holder): - if persistent_holder: - h = m.DataFieldsHolder(2) - for i, exp in enumerate(expected): - c = getattr(h.vec_at(i), core_fld) - assert c.int_value == exp - else: - for i, exp in enumerate(expected): - c = getattr(m.DataFieldsHolder(2).vec_at(i), core_fld) - assert c.int_value == exp diff --git a/tests/test_class_sh_shared_ptr_copy_move.cpp b/tests/test_class_sh_shared_ptr_copy_move.cpp deleted file mode 100644 index 9998327187..0000000000 --- a/tests/test_class_sh_shared_ptr_copy_move.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include -#include -#include - -namespace pybind11_tests { -namespace { - -const std::string fooNames[] = {"ShPtr_", "SmHld_"}; - -template -struct Foo { - std::string history; - explicit Foo(const std::string &history_) : history(history_) {} - Foo(const Foo &other) : history(other.history + "_CpCtor") {} - Foo(Foo &&other) noexcept : history(other.history + "_MvCtor") {} - Foo &operator=(const Foo &other) { - history = other.history + "_OpEqLv"; - return *this; - } - Foo &operator=(Foo &&other) noexcept { - history = other.history + "_OpEqRv"; - return *this; - } - std::string get_history() const { return "Foo" + fooNames[SerNo] + history; } -}; - -using FooShPtr = Foo<0>; -using FooSmHld = Foo<1>; - -struct Outer { - std::shared_ptr ShPtr; - std::shared_ptr SmHld; - Outer() - : ShPtr(std::make_shared("Outer")), SmHld(std::make_shared("Outer")) {} - std::shared_ptr getShPtr() const { return ShPtr; } - std::shared_ptr getSmHld() const { return SmHld; } -}; - -} // namespace -} // namespace pybind11_tests - -PYBIND11_TYPE_CASTER_BASE_HOLDER(pybind11_tests::FooShPtr, - std::shared_ptr) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::FooSmHld) - -namespace pybind11_tests { - -TEST_SUBMODULE(class_sh_shared_ptr_copy_move, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - namespace py = pybind11; - - py::class_>(m, "FooShPtr") - .def("get_history", &FooShPtr::get_history); - py::classh(m, "FooSmHld").def("get_history", &FooSmHld::get_history); - - auto outer = py::class_(m, "Outer").def(py::init()); -# define MAKE_PROP(PropTyp) \ - MAKE_PROP_FOO(ShPtr, PropTyp) \ - MAKE_PROP_FOO(SmHld, PropTyp) - -# define MAKE_PROP_FOO(FooTyp, PropTyp) \ - .def_##PropTyp(#FooTyp "_" #PropTyp "_default", &Outer::FooTyp) \ - .def_##PropTyp( \ - #FooTyp "_" #PropTyp "_copy", &Outer::FooTyp, py::return_value_policy::copy) \ - .def_##PropTyp( \ - #FooTyp "_" #PropTyp "_move", &Outer::FooTyp, py::return_value_policy::move) - outer MAKE_PROP(readonly) MAKE_PROP(readwrite); -# undef MAKE_PROP_FOO - -# define MAKE_PROP_FOO(FooTyp, PropTyp) \ - .def_##PropTyp(#FooTyp "_property_" #PropTyp "_default", &Outer::FooTyp) \ - .def_property_##PropTyp(#FooTyp "_property_" #PropTyp "_copy", \ - &Outer::get##FooTyp, \ - py::return_value_policy::copy) \ - .def_property_##PropTyp(#FooTyp "_property_" #PropTyp "_move", \ - &Outer::get##FooTyp, \ - py::return_value_policy::move) - outer MAKE_PROP(readonly); -# undef MAKE_PROP_FOO -# undef MAKE_PROP - - m.def("test_ShPtr_copy", []() { - auto o = std::make_shared("copy"); - auto l = py::list(); - l.append(o); - return l; - }); - m.def("test_SmHld_copy", []() { - auto o = std::make_shared("copy"); - auto l = py::list(); - l.append(o); - return l; - }); - - m.def("test_ShPtr_move", []() { - auto o = std::make_shared("move"); - auto l = py::list(); - l.append(std::move(o)); - return l; - }); - m.def("test_SmHld_move", []() { - auto o = std::make_shared("move"); - auto l = py::list(); - l.append(std::move(o)); - return l; - }); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} - -} // namespace pybind11_tests diff --git a/tests/test_class_sh_shared_ptr_copy_move.py b/tests/test_class_sh_shared_ptr_copy_move.py deleted file mode 100644 index 70f5c76a02..0000000000 --- a/tests/test_class_sh_shared_ptr_copy_move.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_shared_ptr_copy_move as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_shptr_copy(): - txt = m.test_ShPtr_copy()[0].get_history() - assert txt == "FooShPtr_copy" - - -def test_smhld_copy(): - txt = m.test_SmHld_copy()[0].get_history() - assert txt == "FooSmHld_copy" - - -def test_shptr_move(): - txt = m.test_ShPtr_move()[0].get_history() - assert txt == "FooShPtr_move" - - -def test_smhld_move(): - txt = m.test_SmHld_move()[0].get_history() - assert txt == "FooSmHld_move" - - -def _check_property(foo_typ, prop_typ, policy): - o = m.Outer() - name = f"{foo_typ}_{prop_typ}_{policy}" - history = f"Foo{foo_typ}_Outer" - f = getattr(o, name) - assert f.get_history() == history - # and try again to check that o did not get changed - f = getattr(o, name) - assert f.get_history() == history - - -def test_properties(): - for prop_typ in ("readonly", "readwrite", "property_readonly"): - for foo_typ in ("ShPtr", "SmHld"): - for policy in ("default", "copy", "move"): - _check_property(foo_typ, prop_typ, policy) diff --git a/tests/test_class_sh_trampoline_basic.cpp b/tests/test_class_sh_trampoline_basic.cpp deleted file mode 100644 index 32bf23d2d5..0000000000 --- a/tests/test_class_sh_trampoline_basic.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_trampoline_basic { - -template // Using int as a trick to easily generate a series of types. -struct Abase { - int val = 0; - virtual ~Abase() = default; - explicit Abase(int val_) : val{val_} {} - int Get() const { return val * 10 + 3; } - virtual int Add(int other_val) const = 0; - - // Some compilers complain about implicitly defined versions of some of the following: - Abase(const Abase &) = default; - Abase(Abase &&) noexcept = default; - Abase &operator=(const Abase &) = default; - Abase &operator=(Abase &&) noexcept = default; -}; - -template -struct AbaseAlias : Abase { - using Abase::Abase; - - int Add(int other_val) const override { - PYBIND11_OVERRIDE_PURE(int, /* Return type */ - Abase, /* Parent class */ - Add, /* Name of function in C++ (must match Python name) */ - other_val); - } -}; - -#ifdef PYBIND11_SMART_HOLDER_ENABLED -template <> -struct AbaseAlias<1> : Abase<1>, py::trampoline_self_life_support { - using Abase<1>::Abase; - - int Add(int other_val) const override { - PYBIND11_OVERRIDE_PURE(int, /* Return type */ - Abase<1>, /* Parent class */ - Add, /* Name of function in C++ (must match Python name) */ - other_val); - } -}; -#endif // PYBIND11_SMART_HOLDER_ENABLED - -template -int AddInCppRawPtr(const Abase *obj, int other_val) { - return obj->Add(other_val) * 10 + 7; -} - -template -int AddInCppSharedPtr(std::shared_ptr> obj, int other_val) { - return obj->Add(other_val) * 100 + 11; -} - -template -int AddInCppUniquePtr(std::unique_ptr> obj, int other_val) { - return obj->Add(other_val) * 100 + 13; -} - -#ifdef PYBIND11_SMART_HOLDER_ENABLED -template -void wrap(py::module_ m, const char *py_class_name) { - py::classh, AbaseAlias>(m, py_class_name) - .def(py::init(), py::arg("val")) - .def("Get", &Abase::Get) - .def("Add", &Abase::Add, py::arg("other_val")); - - m.def("AddInCppRawPtr", AddInCppRawPtr, py::arg("obj"), py::arg("other_val")); - m.def("AddInCppSharedPtr", AddInCppSharedPtr, py::arg("obj"), py::arg("other_val")); - m.def("AddInCppUniquePtr", AddInCppUniquePtr, py::arg("obj"), py::arg("other_val")); -} -#endif - -} // namespace class_sh_trampoline_basic -} // namespace pybind11_tests - -using namespace pybind11_tests::class_sh_trampoline_basic; - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(Abase<0>) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(Abase<1>) - -TEST_SUBMODULE(class_sh_trampoline_basic, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - wrap<0>(m, "Abase0"); - wrap<1>(m, "Abase1"); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_trampoline_basic.py b/tests/test_class_sh_trampoline_basic.py deleted file mode 100644 index 935a17275b..0000000000 --- a/tests/test_class_sh_trampoline_basic.py +++ /dev/null @@ -1,62 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_trampoline_basic as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -class PyDrvd0(m.Abase0): - def __init__(self, val): - super().__init__(val) - - def Add(self, other_val): - return self.Get() * 100 + other_val - - -class PyDrvd1(m.Abase1): - def __init__(self, val): - super().__init__(val) - - def Add(self, other_val): - return self.Get() * 200 + other_val - - -def test_drvd0_add(): - drvd = PyDrvd0(74) - assert drvd.Add(38) == (74 * 10 + 3) * 100 + 38 - - -def test_drvd0_add_in_cpp_raw_ptr(): - drvd = PyDrvd0(52) - assert m.AddInCppRawPtr(drvd, 27) == ((52 * 10 + 3) * 100 + 27) * 10 + 7 - - -def test_drvd0_add_in_cpp_shared_ptr(): - while True: - drvd = PyDrvd0(36) - assert m.AddInCppSharedPtr(drvd, 56) == ((36 * 10 + 3) * 100 + 56) * 100 + 11 - return # Comment out for manual leak checking (use `top` command). - - -def test_drvd0_add_in_cpp_unique_ptr(): - while True: - drvd = PyDrvd0(0) - with pytest.raises(ValueError) as exc_info: - m.AddInCppUniquePtr(drvd, 0) - assert ( - str(exc_info.value) - == "Alias class (also known as trampoline) does not inherit from" - " py::trampoline_self_life_support, therefore the ownership of this" - " instance cannot safely be transferred to C++." - ) - return # Comment out for manual leak checking (use `top` command). - - -def test_drvd1_add_in_cpp_unique_ptr(): - while True: - drvd = PyDrvd1(25) - assert m.AddInCppUniquePtr(drvd, 83) == ((25 * 10 + 3) * 200 + 83) * 100 + 13 - return # Comment out for manual leak checking (use `top` command). diff --git a/tests/test_class_sh_trampoline_self_life_support.cpp b/tests/test_class_sh_trampoline_self_life_support.cpp deleted file mode 100644 index c342598e51..0000000000 --- a/tests/test_class_sh_trampoline_self_life_support.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2021 The Pybind Development Team. -// All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "pybind11/smart_holder.h" -#include "pybind11/trampoline_self_life_support.h" -#include "pybind11_tests.h" - -#include -#include -#include - -namespace pybind11_tests { -namespace class_sh_trampoline_self_life_support { - -struct Big5 { // Also known as "rule of five". - std::string history; - - explicit Big5(std::string history_start) : history{std::move(history_start)} {} - - Big5(const Big5 &other) { history = other.history + "_CpCtor"; } - - Big5(Big5 &&other) noexcept { history = other.history + "_MvCtor"; } - - Big5 &operator=(const Big5 &other) { - history = other.history + "_OpEqLv"; - return *this; - } - - Big5 &operator=(Big5 &&other) noexcept { - history = other.history + "_OpEqRv"; - return *this; - } - - virtual ~Big5() = default; - -protected: - Big5() : history{"DefaultConstructor"} {} -}; - -#ifdef PYBIND11_SMART_HOLDER_ENABLED -struct Big5Trampoline : Big5, py::trampoline_self_life_support { - using Big5::Big5; -}; -#endif - -} // namespace class_sh_trampoline_self_life_support -} // namespace pybind11_tests - -using namespace pybind11_tests::class_sh_trampoline_self_life_support; - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(Big5) - -TEST_SUBMODULE(class_sh_trampoline_self_life_support, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - py::classh(m, "Big5") - .def(py::init()) - .def_readonly("history", &Big5::history); - - m.def("action", [](std::unique_ptr obj, int action_id) { - py::object o2 = py::none(); - // This is very unusual, but needed to directly exercise the trampoline_self_life_support - // CpCtor, MvCtor, operator= lvalue, operator= rvalue. - auto *obj_trampoline = dynamic_cast(obj.get()); - if (obj_trampoline != nullptr) { - switch (action_id) { - case 0: { // CpCtor - std::unique_ptr cp(new Big5Trampoline(*obj_trampoline)); - o2 = py::cast(std::move(cp)); - } break; - case 1: { // MvCtor - std::unique_ptr mv(new Big5Trampoline(std::move(*obj_trampoline))); - o2 = py::cast(std::move(mv)); - } break; - case 2: { // operator= lvalue - std::unique_ptr lv(new Big5Trampoline); - *lv = *obj_trampoline; // NOLINT clang-tidy cppcoreguidelines-slicing - o2 = py::cast(std::move(lv)); - } break; - case 3: { // operator= rvalue - std::unique_ptr rv(new Big5Trampoline); - *rv = std::move(*obj_trampoline); - o2 = py::cast(std::move(rv)); - } break; - default: - break; - } - } - py::object o1 = py::cast(std::move(obj)); - return py::make_tuple(o1, o2); - }); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_trampoline_self_life_support.py b/tests/test_class_sh_trampoline_self_life_support.py deleted file mode 100644 index 076f3007d9..0000000000 --- a/tests/test_class_sh_trampoline_self_life_support.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import annotations - -import pytest - -import pybind11_tests.class_sh_trampoline_self_life_support as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -class PyBig5(m.Big5): - pass - - -def test_m_big5(): - obj = m.Big5("Seed") - assert obj.history == "Seed" - o1, o2 = m.action(obj, 0) - assert o1 is not obj - assert o1.history == "Seed" - with pytest.raises(ValueError) as excinfo: - _ = obj.history - assert "Python instance was disowned" in str(excinfo.value) - assert o2 is None - - -@pytest.mark.parametrize( - ("action_id", "expected_history"), - [ - (0, "Seed_CpCtor"), - (1, "Seed_MvCtor"), - (2, "Seed_OpEqLv"), - (3, "Seed_OpEqRv"), - ], -) -def test_py_big5(action_id, expected_history): - obj = PyBig5("Seed") - assert obj.history == "Seed" - o1, o2 = m.action(obj, action_id) - assert o1 is obj - assert o2.history == expected_history diff --git a/tests/test_class_sh_trampoline_shared_from_this.cpp b/tests/test_class_sh_trampoline_shared_from_this.cpp deleted file mode 100644 index d09fe0041f..0000000000 --- a/tests/test_class_sh_trampoline_shared_from_this.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2021 The Pybind Development Team. -// All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "pybind11/smart_holder.h" -#include "pybind11_tests.h" - -#include -#include - -namespace pybind11_tests { -namespace class_sh_trampoline_shared_from_this { - -struct Sft : std::enable_shared_from_this { - std::string history; - explicit Sft(const std::string &history_seed) : history{history_seed} {} - virtual ~Sft() = default; - -#if defined(__clang__) - // "Group of 4" begin. - // This group is not meant to be used, but will leave a trace in the - // history in case something goes wrong. - // However, compilers other than clang have a variety of issues. It is not - // worth the trouble covering all platforms. - Sft(const Sft &other) : enable_shared_from_this(other) { history = other.history + "_CpCtor"; } - - Sft(Sft &&other) noexcept { history = other.history + "_MvCtor"; } - - Sft &operator=(const Sft &other) { - history = other.history + "_OpEqLv"; - return *this; - } - - Sft &operator=(Sft &&other) noexcept { - history = other.history + "_OpEqRv"; - return *this; - } - // "Group of 4" end. -#endif -}; - -struct SftSharedPtrStash { - int ser_no; - std::vector> stash; - explicit SftSharedPtrStash(int ser_no) : ser_no{ser_no} {} - void Clear() { stash.clear(); } - void Add(const std::shared_ptr &obj) { - if (!obj->history.empty()) { - obj->history += "_Stash" + std::to_string(ser_no) + "Add"; - } - stash.push_back(obj); - } - void AddSharedFromThis(Sft *obj) { - auto sft = obj->shared_from_this(); - if (!sft->history.empty()) { - sft->history += "_Stash" + std::to_string(ser_no) + "AddSharedFromThis"; - } - stash.push_back(sft); - } - std::string history(unsigned i) { - if (i < stash.size()) { - return stash[i]->history; - } - return "OutOfRange"; - } - long use_count(unsigned i) { - if (i < stash.size()) { - return stash[i].use_count(); - } - return -1; - } -}; - -#ifdef PYBIND11_SMART_HOLDER_ENABLED -struct SftTrampoline : Sft, py::trampoline_self_life_support { - using Sft::Sft; -}; -#endif - -long use_count(const std::shared_ptr &obj) { return obj.use_count(); } - -long pass_shared_ptr(const std::shared_ptr &obj) { - auto sft = obj->shared_from_this(); - if (!sft->history.empty()) { - sft->history += "_PassSharedPtr"; - } - return sft.use_count(); -} - -std::string pass_unique_ptr_cref(const std::unique_ptr &obj) { - return obj ? obj->history : ""; -} -void pass_unique_ptr_rref(std::unique_ptr &&) { - throw std::runtime_error("Expected to not be reached."); -} - -Sft *make_pure_cpp_sft_raw_ptr(const std::string &history_seed) { return new Sft{history_seed}; } - -std::unique_ptr make_pure_cpp_sft_unq_ptr(const std::string &history_seed) { - return std::unique_ptr(new Sft{history_seed}); -} - -std::shared_ptr make_pure_cpp_sft_shd_ptr(const std::string &history_seed) { - return std::make_shared(history_seed); -} - -std::shared_ptr pass_through_shd_ptr(const std::shared_ptr &obj) { return obj; } - -} // namespace class_sh_trampoline_shared_from_this -} // namespace pybind11_tests - -using namespace pybind11_tests::class_sh_trampoline_shared_from_this; - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(Sft) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(SftSharedPtrStash) - -TEST_SUBMODULE(class_sh_trampoline_shared_from_this, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - py::classh(m, "Sft") - .def(py::init()) - .def(py::init([](const std::string &history, int) { - return std::make_shared(history); - })) - .def_readonly("history", &Sft::history) - // This leads to multiple entries in registered_instances: - .def(py::init([](const std::shared_ptr &existing) { return existing; })); - - py::classh(m, "SftSharedPtrStash") - .def(py::init()) - .def("Clear", &SftSharedPtrStash::Clear) - .def("Add", &SftSharedPtrStash::Add) - .def("AddSharedFromThis", &SftSharedPtrStash::AddSharedFromThis) - .def("history", &SftSharedPtrStash::history) - .def("use_count", &SftSharedPtrStash::use_count); - - m.def("use_count", use_count); - m.def("pass_shared_ptr", pass_shared_ptr); - m.def("pass_unique_ptr_cref", pass_unique_ptr_cref); - m.def("pass_unique_ptr_rref", pass_unique_ptr_rref); - m.def("make_pure_cpp_sft_raw_ptr", make_pure_cpp_sft_raw_ptr); - m.def("make_pure_cpp_sft_unq_ptr", make_pure_cpp_sft_unq_ptr); - m.def("make_pure_cpp_sft_shd_ptr", make_pure_cpp_sft_shd_ptr); - m.def("pass_through_shd_ptr", pass_through_shd_ptr); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_trampoline_shared_from_this.py b/tests/test_class_sh_trampoline_shared_from_this.py deleted file mode 100644 index 9b0d122b40..0000000000 --- a/tests/test_class_sh_trampoline_shared_from_this.py +++ /dev/null @@ -1,250 +0,0 @@ -from __future__ import annotations - -import sys -import weakref - -import pytest - -import env -import pybind11_tests.class_sh_trampoline_shared_from_this as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -class PySft(m.Sft): - pass - - -def test_release_and_shared_from_this(): - # Exercises the most direct path from building a shared_from_this-visible - # shared_ptr to calling shared_from_this. - obj = PySft("PySft") - assert obj.history == "PySft" - assert m.use_count(obj) == 1 - assert m.pass_shared_ptr(obj) == 2 - assert obj.history == "PySft_PassSharedPtr" - assert m.use_count(obj) == 1 - assert m.pass_shared_ptr(obj) == 2 - assert obj.history == "PySft_PassSharedPtr_PassSharedPtr" - assert m.use_count(obj) == 1 - - -def test_release_and_shared_from_this_leak(): - obj = PySft("") - while True: - m.pass_shared_ptr(obj) - assert not obj.history - assert m.use_count(obj) == 1 - break # Comment out for manual leak checking (use `top` command). - - -def test_release_and_stash(): - # Exercises correct functioning of guarded_delete weak_ptr. - obj = PySft("PySft") - stash1 = m.SftSharedPtrStash(1) - stash1.Add(obj) - exp_hist = "PySft_Stash1Add" - assert obj.history == exp_hist - assert m.use_count(obj) == 2 - assert stash1.history(0) == exp_hist - assert stash1.use_count(0) == 1 - assert m.pass_shared_ptr(obj) == 3 - exp_hist += "_PassSharedPtr" - assert obj.history == exp_hist - assert m.use_count(obj) == 2 - assert stash1.history(0) == exp_hist - assert stash1.use_count(0) == 1 - stash2 = m.SftSharedPtrStash(2) - stash2.Add(obj) - exp_hist += "_Stash2Add" - assert obj.history == exp_hist - assert m.use_count(obj) == 3 - assert stash2.history(0) == exp_hist - assert stash2.use_count(0) == 2 - stash2.Add(obj) - exp_hist += "_Stash2Add" - assert obj.history == exp_hist - assert m.use_count(obj) == 4 - assert stash1.history(0) == exp_hist - assert stash1.use_count(0) == 3 - assert stash2.history(0) == exp_hist - assert stash2.use_count(0) == 3 - assert stash2.history(1) == exp_hist - assert stash2.use_count(1) == 3 - del obj - assert stash2.history(0) == exp_hist - assert stash2.use_count(0) == 3 - assert stash2.history(1) == exp_hist - assert stash2.use_count(1) == 3 - stash2.Clear() - assert stash1.history(0) == exp_hist - assert stash1.use_count(0) == 1 - - -def test_release_and_stash_leak(): - obj = PySft("") - while True: - stash1 = m.SftSharedPtrStash(1) - stash1.Add(obj) - assert not obj.history - assert m.use_count(obj) == 2 - assert stash1.use_count(0) == 1 - stash1.Add(obj) - assert not obj.history - assert m.use_count(obj) == 3 - assert stash1.use_count(0) == 2 - assert stash1.use_count(1) == 2 - break # Comment out for manual leak checking (use `top` command). - - -def test_release_and_stash_via_shared_from_this(): - # Exercises that the smart_holder vptr is invisible to the shared_from_this mechanism. - obj = PySft("PySft") - stash1 = m.SftSharedPtrStash(1) - with pytest.raises(RuntimeError) as exc_info: - stash1.AddSharedFromThis(obj) - assert str(exc_info.value) == "bad_weak_ptr" - stash1.Add(obj) - assert obj.history == "PySft_Stash1Add" - assert stash1.use_count(0) == 1 - stash1.AddSharedFromThis(obj) - assert obj.history == "PySft_Stash1Add_Stash1AddSharedFromThis" - assert stash1.use_count(0) == 2 - assert stash1.use_count(1) == 2 - - -def test_release_and_stash_via_shared_from_this_leak(): - obj = PySft("") - while True: - stash1 = m.SftSharedPtrStash(1) - with pytest.raises(RuntimeError) as exc_info: - stash1.AddSharedFromThis(obj) - assert str(exc_info.value) == "bad_weak_ptr" - stash1.Add(obj) - assert not obj.history - assert stash1.use_count(0) == 1 - stash1.AddSharedFromThis(obj) - assert not obj.history - assert stash1.use_count(0) == 2 - assert stash1.use_count(1) == 2 - break # Comment out for manual leak checking (use `top` command). - - -def test_pass_released_shared_ptr_as_unique_ptr(): - # Exercises that returning a unique_ptr fails while a shared_from_this - # visible shared_ptr exists. - obj = PySft("PySft") - stash1 = m.SftSharedPtrStash(1) - stash1.Add(obj) # Releases shared_ptr to C++. - assert m.pass_unique_ptr_cref(obj) == "PySft_Stash1Add" - assert obj.history == "PySft_Stash1Add" - with pytest.raises(ValueError) as exc_info: - m.pass_unique_ptr_rref(obj) - assert str(exc_info.value) == ( - "Python instance is currently owned by a std::shared_ptr." - ) - assert obj.history == "PySft_Stash1Add" - - -@pytest.mark.parametrize( - "make_f", - [ - m.make_pure_cpp_sft_raw_ptr, - m.make_pure_cpp_sft_unq_ptr, - m.make_pure_cpp_sft_shd_ptr, - ], -) -def test_pure_cpp_sft_raw_ptr(make_f): - # Exercises void_cast_raw_ptr logic for different situations. - obj = make_f("PureCppSft") - assert m.pass_shared_ptr(obj) == 3 - assert obj.history == "PureCppSft_PassSharedPtr" - obj = make_f("PureCppSft") - stash1 = m.SftSharedPtrStash(1) - stash1.AddSharedFromThis(obj) - assert obj.history == "PureCppSft_Stash1AddSharedFromThis" - - -def test_multiple_registered_instances_for_same_pointee(): - obj0 = PySft("PySft") - obj0.attachment_in_dict = "Obj0" - assert m.pass_through_shd_ptr(obj0) is obj0 - while True: - obj = m.Sft(obj0) - assert obj is not obj0 - obj_pt = m.pass_through_shd_ptr(obj) - # Unpredictable! Because registered_instances is as std::unordered_multimap. - assert obj_pt is obj0 or obj_pt is obj - # Multiple registered_instances for the same pointee can lead to unpredictable results: - if obj_pt is obj0: - assert obj_pt.attachment_in_dict == "Obj0" - else: - assert not hasattr(obj_pt, "attachment_in_dict") - assert obj0.history == "PySft" - break # Comment out for manual leak checking (use `top` command). - - -def test_multiple_registered_instances_for_same_pointee_leak(): - obj0 = PySft("") - while True: - stash1 = m.SftSharedPtrStash(1) - stash1.Add(m.Sft(obj0)) - assert stash1.use_count(0) == 1 - stash1.Add(m.Sft(obj0)) - assert stash1.use_count(0) == 1 - assert stash1.use_count(1) == 1 - assert not obj0.history - break # Comment out for manual leak checking (use `top` command). - - -def test_multiple_registered_instances_for_same_pointee_recursive(): - while True: - obj0 = PySft("PySft") - if not env.PYPY: - obj0_wr = weakref.ref(obj0) - obj = obj0 - # This loop creates a chain of instances linked by shared_ptrs. - for _ in range(10): - obj_next = m.Sft(obj) - assert obj_next is not obj - obj = obj_next - del obj_next - assert obj.history == "PySft" - del obj0 - if not env.PYPY and not env.GRAALPY: - assert obj0_wr() is not None - del obj # This releases the chain recursively. - if not env.PYPY and not env.GRAALPY: - assert obj0_wr() is None - break # Comment out for manual leak checking (use `top` command). - - -# As of 2021-07-10 the pybind11 GitHub Actions valgrind build uses Python 3.9. -WORKAROUND_ENABLING_ROLLBACK_OF_PR3068 = env.LINUX and sys.version_info == (3, 9) - - -def test_std_make_shared_factory(): - class PySftMakeShared(m.Sft): - def __init__(self, history): - super().__init__(history, 0) - - obj = PySftMakeShared("PySftMakeShared") - assert obj.history == "PySftMakeShared" - if WORKAROUND_ENABLING_ROLLBACK_OF_PR3068: - try: - m.pass_through_shd_ptr(obj) - except RuntimeError as e: - str_exc_info_value = str(e) - else: - str_exc_info_value = "RuntimeError NOT RAISED" - else: - with pytest.raises(RuntimeError) as exc_info: - m.pass_through_shd_ptr(obj) - str_exc_info_value = str(exc_info.value) - assert ( - str_exc_info_value - == "smart_holder_type_casters load_as_shared_ptr failure: not implemented:" - " trampoline-self-life-support for external shared_ptr to type inheriting" - " from std::enable_shared_from_this." - ) diff --git a/tests/test_class_sh_trampoline_shared_ptr_cpp_arg.cpp b/tests/test_class_sh_trampoline_shared_ptr_cpp_arg.cpp deleted file mode 100644 index ced2ea9dd1..0000000000 --- a/tests/test_class_sh_trampoline_shared_ptr_cpp_arg.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2021 The Pybind Development Team. -// All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "pybind11/smart_holder.h" -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_trampoline_shared_ptr_cpp_arg { - -// For testing whether a python subclass of a C++ object dies when the -// last python reference is lost -struct SpBase { - // returns true if the base virtual function is called - virtual bool is_base_used() { return true; } - - // returns true if there's an associated python instance - bool has_python_instance() { - auto *tinfo = py::detail::get_type_info(typeid(SpBase)); - return (bool) py::detail::get_object_handle(this, tinfo); - } - - SpBase() = default; - SpBase(const SpBase &) = delete; - virtual ~SpBase() = default; -}; - -std::shared_ptr pass_through_shd_ptr(const std::shared_ptr &obj) { return obj; } - -struct PySpBase : SpBase { - using SpBase::SpBase; - bool is_base_used() override { PYBIND11_OVERRIDE(bool, SpBase, is_base_used); } -}; - -struct SpBaseTester { - std::shared_ptr get_object() const { return m_obj; } - void set_object(std::shared_ptr obj) { m_obj = std::move(obj); } - bool is_base_used() { return m_obj->is_base_used(); } - bool has_instance() { return (bool) m_obj; } - bool has_python_instance() { return m_obj && m_obj->has_python_instance(); } - void set_nonpython_instance() { m_obj = std::make_shared(); } - std::shared_ptr m_obj; -}; - -// For testing that a C++ class without an alias does not retain the python -// portion of the object -struct SpGoAway {}; - -struct SpGoAwayTester { - std::shared_ptr m_obj; -}; - -} // namespace class_sh_trampoline_shared_ptr_cpp_arg -} // namespace pybind11_tests - -using namespace pybind11_tests::class_sh_trampoline_shared_ptr_cpp_arg; - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpBase) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpBaseTester) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpGoAway) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(SpGoAwayTester) - -TEST_SUBMODULE(class_sh_trampoline_shared_ptr_cpp_arg, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - // For testing whether a python subclass of a C++ object dies when the - // last python reference is lost - - py::classh(m, "SpBase") - .def(py::init<>()) - .def(py::init([](int) { return std::make_shared(); })) - .def("is_base_used", &SpBase::is_base_used) - .def("has_python_instance", &SpBase::has_python_instance); - - m.def("pass_through_shd_ptr", pass_through_shd_ptr); - m.def("pass_through_shd_ptr_release_gil", - pass_through_shd_ptr, - py::call_guard()); // PR #4196 - - py::classh(m, "SpBaseTester") - .def(py::init<>()) - .def("get_object", &SpBaseTester::get_object) - .def("set_object", &SpBaseTester::set_object) - .def("is_base_used", &SpBaseTester::is_base_used) - .def("has_instance", &SpBaseTester::has_instance) - .def("has_python_instance", &SpBaseTester::has_python_instance) - .def("set_nonpython_instance", &SpBaseTester::set_nonpython_instance) - .def_readwrite("obj", &SpBaseTester::m_obj); - - // For testing that a C++ class without an alias does not retain the python - // portion of the object - - py::classh(m, "SpGoAway").def(py::init<>()); - - py::classh(m, "SpGoAwayTester") - .def(py::init<>()) - .def_readwrite("obj", &SpGoAwayTester::m_obj); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_trampoline_shared_ptr_cpp_arg.py b/tests/test_class_sh_trampoline_shared_ptr_cpp_arg.py deleted file mode 100644 index 7a44a602d4..0000000000 --- a/tests/test_class_sh_trampoline_shared_ptr_cpp_arg.py +++ /dev/null @@ -1,157 +0,0 @@ -from __future__ import annotations - -import pytest - -import env # noqa: F401 -import pybind11_tests.class_sh_trampoline_shared_ptr_cpp_arg as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") -def test_shared_ptr_cpp_arg(): - import weakref - - class PyChild(m.SpBase): - def is_base_used(self): - return False - - tester = m.SpBaseTester() - - obj = PyChild() - objref = weakref.ref(obj) - - # Pass the last python reference to the C++ function - tester.set_object(obj) - del obj - pytest.gc_collect() - - # python reference is still around since C++ has it now - assert objref() is not None - assert tester.is_base_used() is False - assert tester.obj.is_base_used() is False - assert tester.get_object() is objref() - - -@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") -def test_shared_ptr_cpp_prop(): - class PyChild(m.SpBase): - def is_base_used(self): - return False - - tester = m.SpBaseTester() - - # Set the last python reference as a property of the C++ object - tester.obj = PyChild() - pytest.gc_collect() - - # python reference is still around since C++ has it now - assert tester.is_base_used() is False - assert tester.has_python_instance() is True - assert tester.obj.is_base_used() is False - assert tester.obj.has_python_instance() is True - - -@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") -def test_shared_ptr_arg_identity(): - import weakref - - tester = m.SpBaseTester() - - obj = m.SpBase() - objref = weakref.ref(obj) - - tester.set_object(obj) - del obj - pytest.gc_collect() - - # NOTE: the behavior below is DIFFERENT from PR #2839 - # python reference is gone because it is not an Alias instance - assert objref() is None - assert tester.has_python_instance() is False - - -@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") -def test_shared_ptr_alias_nonpython(): - tester = m.SpBaseTester() - - # C++ creates the object, a python instance shouldn't exist - tester.set_nonpython_instance() - assert tester.is_base_used() is True - assert tester.has_instance() is True - assert tester.has_python_instance() is False - - # Now a python instance exists - cobj = tester.get_object() - assert cobj.has_python_instance() - assert tester.has_instance() is True - assert tester.has_python_instance() is True - - # Now it's gone - del cobj - pytest.gc_collect() - assert tester.has_instance() is True - assert tester.has_python_instance() is False - - # When we pass it as an arg to a new tester the python instance should - # disappear because it wasn't created with an alias - new_tester = m.SpBaseTester() - - cobj = tester.get_object() - assert cobj.has_python_instance() - - new_tester.set_object(cobj) - assert tester.has_python_instance() is True - assert new_tester.has_python_instance() is True - - del cobj - pytest.gc_collect() - - # Gone! - assert tester.has_instance() is True - assert tester.has_python_instance() is False - assert new_tester.has_instance() is True - assert new_tester.has_python_instance() is False - - -@pytest.mark.skipif("env.GRAALPY", reason="Cannot reliably trigger GC") -def test_shared_ptr_goaway(): - import weakref - - tester = m.SpGoAwayTester() - - obj = m.SpGoAway() - objref = weakref.ref(obj) - - assert tester.obj is None - - tester.obj = obj - del obj - pytest.gc_collect() - - # python reference is no longer around - assert objref() is None - # C++ reference is still around - assert tester.obj is not None - - -def test_infinite(): - tester = m.SpBaseTester() - while True: - tester.set_object(m.SpBase()) - break # Comment out for manual leak checking (use `top` command). - - -@pytest.mark.parametrize( - "pass_through_func", [m.pass_through_shd_ptr, m.pass_through_shd_ptr_release_gil] -) -def test_std_make_shared_factory(pass_through_func): - class PyChild(m.SpBase): - def __init__(self): - super().__init__(0) - - obj = PyChild() - while True: - assert pass_through_func(obj) is obj - break # Comment out for manual leak checking (use `top` command). diff --git a/tests/test_class_sh_trampoline_unique_ptr.cpp b/tests/test_class_sh_trampoline_unique_ptr.cpp deleted file mode 100644 index 72c0ca5e02..0000000000 --- a/tests/test_class_sh_trampoline_unique_ptr.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2021 The Pybind Development Team. -// All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "pybind11/smart_holder.h" -#include "pybind11/trampoline_self_life_support.h" -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_trampoline_unique_ptr { - -class Class { -public: - virtual ~Class() = default; - - void setVal(std::uint64_t val) { val_ = val; } - std::uint64_t getVal() const { return val_; } - - virtual std::unique_ptr clone() const = 0; - virtual int foo() const = 0; - -protected: - Class() = default; - - // Some compilers complain about implicitly defined versions of some of the following: - Class(const Class &) = default; - -private: - std::uint64_t val_ = 0; -}; - -} // namespace class_sh_trampoline_unique_ptr -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_trampoline_unique_ptr::Class) - -namespace pybind11_tests { -namespace class_sh_trampoline_unique_ptr { - -#ifdef PYBIND11_SMART_HOLDER_ENABLED -class PyClass : public Class, public py::trampoline_self_life_support { -public: - std::unique_ptr clone() const override { - PYBIND11_OVERRIDE_PURE(std::unique_ptr, Class, clone); - } - - int foo() const override { PYBIND11_OVERRIDE_PURE(int, Class, foo); } -}; -#endif - -} // namespace class_sh_trampoline_unique_ptr -} // namespace pybind11_tests - -TEST_SUBMODULE(class_sh_trampoline_unique_ptr, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - using namespace pybind11_tests::class_sh_trampoline_unique_ptr; - - py::classh(m, "Class") - .def(py::init<>()) - .def("set_val", &Class::setVal) - .def("get_val", &Class::getVal) - .def("clone", &Class::clone) - .def("foo", &Class::foo); - - m.def("clone", [](const Class &obj) { return obj.clone(); }); - m.def("clone_and_foo", [](const Class &obj) { return obj.clone()->foo(); }); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_trampoline_unique_ptr.py b/tests/test_class_sh_trampoline_unique_ptr.py deleted file mode 100644 index 4044761fb1..0000000000 --- a/tests/test_class_sh_trampoline_unique_ptr.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -import pytest - -import pybind11_tests.class_sh_trampoline_unique_ptr as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -class MyClass(m.Class): - def foo(self): - return 10 + self.get_val() - - def clone(self): - cloned = MyClass() - cloned.set_val(self.get_val() + 3) - return cloned - - -def test_m_clone(): - obj = MyClass() - while True: - obj.set_val(5) - obj = m.clone(obj) - assert obj.get_val() == 5 + 3 - assert obj.foo() == 10 + 5 + 3 - return # Comment out for manual leak checking (use `top` command). - - -def test_m_clone_and_foo(): - obj = MyClass() - obj.set_val(7) - while True: - assert m.clone_and_foo(obj) == 10 + 7 + 3 - return # Comment out for manual leak checking (use `top` command). diff --git a/tests/test_class_sh_unique_ptr_custom_deleter.cpp b/tests/test_class_sh_unique_ptr_custom_deleter.cpp deleted file mode 100644 index 4bc4566bf1..0000000000 --- a/tests/test_class_sh_unique_ptr_custom_deleter.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_unique_ptr_custom_deleter { - -// Reduced from a PyCLIF use case in the wild by @wangxf123456. -class Pet { -public: - using Ptr = std::unique_ptr>; - - std::string name; - - static Ptr New(const std::string &name) { - return Ptr(new Pet(name), std::default_delete()); - } - -private: - explicit Pet(const std::string &name) : name(name) {} -}; - -} // namespace class_sh_unique_ptr_custom_deleter -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_unique_ptr_custom_deleter::Pet) - -namespace pybind11_tests { -namespace class_sh_unique_ptr_custom_deleter { - -TEST_SUBMODULE(class_sh_unique_ptr_custom_deleter, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - py::classh(m, "Pet").def_readwrite("name", &Pet::name); - - m.def("create", &Pet::New); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} - -} // namespace class_sh_unique_ptr_custom_deleter -} // namespace pybind11_tests diff --git a/tests/test_class_sh_unique_ptr_custom_deleter.py b/tests/test_class_sh_unique_ptr_custom_deleter.py deleted file mode 100644 index 8658de8d10..0000000000 --- a/tests/test_class_sh_unique_ptr_custom_deleter.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_unique_ptr_custom_deleter as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_create(): - pet = m.create("abc") - assert pet.name == "abc" diff --git a/tests/test_class_sh_unique_ptr_member.cpp b/tests/test_class_sh_unique_ptr_member.cpp deleted file mode 100644 index 90df0c62c7..0000000000 --- a/tests/test_class_sh_unique_ptr_member.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_unique_ptr_member { - -class pointee { // NOT copyable. -public: - pointee() = default; - - int get_int() const { return 213; } - - pointee(const pointee &) = delete; - pointee(pointee &&) = delete; - pointee &operator=(const pointee &) = delete; - pointee &operator=(pointee &&) = delete; -}; - -inline std::unique_ptr make_unique_pointee() { - return std::unique_ptr(new pointee); -} - -class ptr_owner { -public: - explicit ptr_owner(std::unique_ptr ptr) : ptr_(std::move(ptr)) {} - - bool is_owner() const { return bool(ptr_); } - - std::unique_ptr give_up_ownership_via_unique_ptr() { return std::move(ptr_); } - std::shared_ptr give_up_ownership_via_shared_ptr() { return std::move(ptr_); } - -private: - std::unique_ptr ptr_; -}; - -} // namespace class_sh_unique_ptr_member -} // namespace pybind11_tests - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_unique_ptr_member::pointee) - -namespace pybind11_tests { -namespace class_sh_unique_ptr_member { - -TEST_SUBMODULE(class_sh_unique_ptr_member, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - py::classh(m, "pointee").def(py::init<>()).def("get_int", &pointee::get_int); - - m.def("make_unique_pointee", make_unique_pointee); - - py::class_(m, "ptr_owner") - .def(py::init>(), py::arg("ptr")) - .def("is_owner", &ptr_owner::is_owner) - .def("give_up_ownership_via_unique_ptr", &ptr_owner::give_up_ownership_via_unique_ptr) - .def("give_up_ownership_via_shared_ptr", &ptr_owner::give_up_ownership_via_shared_ptr); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} - -} // namespace class_sh_unique_ptr_member -} // namespace pybind11_tests diff --git a/tests/test_class_sh_unique_ptr_member.py b/tests/test_class_sh_unique_ptr_member.py deleted file mode 100644 index 25bcba9763..0000000000 --- a/tests/test_class_sh_unique_ptr_member.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_unique_ptr_member as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -def test_make_unique_pointee(): - obj = m.make_unique_pointee() - assert obj.get_int() == 213 - - -@pytest.mark.parametrize( - "give_up_ownership_via", - ["give_up_ownership_via_unique_ptr", "give_up_ownership_via_shared_ptr"], -) -def test_pointee_and_ptr_owner(give_up_ownership_via): - obj = m.pointee() - assert obj.get_int() == 213 - owner = m.ptr_owner(obj) - with pytest.raises(ValueError, match="Python instance was disowned"): - obj.get_int() - assert owner.is_owner() - reclaimed = getattr(owner, give_up_ownership_via)() - assert not owner.is_owner() - assert reclaimed.get_int() == 213 diff --git a/tests/test_class_sh_virtual_py_cpp_mix.cpp b/tests/test_class_sh_virtual_py_cpp_mix.cpp deleted file mode 100644 index 28f7d8fd60..0000000000 --- a/tests/test_class_sh_virtual_py_cpp_mix.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include - -#include "pybind11_tests.h" - -#include - -namespace pybind11_tests { -namespace class_sh_virtual_py_cpp_mix { - -class Base { -public: - virtual ~Base() = default; - virtual int get() const { return 101; } - - // Some compilers complain about implicitly defined versions of some of the following: - Base() = default; - Base(const Base &) = default; -}; - -class CppDerivedPlain : public Base { -public: - int get() const override { return 202; } -}; - -class CppDerived : public Base { -public: - int get() const override { return 212; } -}; - -int get_from_cpp_plainc_ptr(const Base *b) { return b->get() + 4000; } - -int get_from_cpp_unique_ptr(std::unique_ptr b) { return b->get() + 5000; } - -#ifdef PYBIND11_SMART_HOLDER_ENABLED - -struct BaseVirtualOverrider : Base, py::trampoline_self_life_support { - using Base::Base; - - int get() const override { PYBIND11_OVERRIDE(int, Base, get); } -}; - -struct CppDerivedVirtualOverrider : CppDerived, py::trampoline_self_life_support { - using CppDerived::CppDerived; - - int get() const override { PYBIND11_OVERRIDE(int, CppDerived, get); } -}; - -#endif - -} // namespace class_sh_virtual_py_cpp_mix -} // namespace pybind11_tests - -using namespace pybind11_tests::class_sh_virtual_py_cpp_mix; - -PYBIND11_SMART_HOLDER_TYPE_CASTERS(Base) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerivedPlain) -PYBIND11_SMART_HOLDER_TYPE_CASTERS(CppDerived) - -TEST_SUBMODULE(class_sh_virtual_py_cpp_mix, m) { - m.attr("defined_PYBIND11_SMART_HOLDER_ENABLED") = -#ifndef PYBIND11_SMART_HOLDER_ENABLED - false; -#else - true; - - py::classh(m, "Base").def(py::init<>()).def("get", &Base::get); - - py::classh(m, "CppDerivedPlain").def(py::init<>()); - - py::classh(m, "CppDerived").def(py::init<>()); - - m.def("get_from_cpp_plainc_ptr", get_from_cpp_plainc_ptr, py::arg("b")); - m.def("get_from_cpp_unique_ptr", get_from_cpp_unique_ptr, py::arg("b")); -#endif // PYBIND11_SMART_HOLDER_ENABLED -} diff --git a/tests/test_class_sh_virtual_py_cpp_mix.py b/tests/test_class_sh_virtual_py_cpp_mix.py deleted file mode 100644 index 937a15ab46..0000000000 --- a/tests/test_class_sh_virtual_py_cpp_mix.py +++ /dev/null @@ -1,69 +0,0 @@ -from __future__ import annotations - -import pytest - -from pybind11_tests import class_sh_virtual_py_cpp_mix as m - -if not m.defined_PYBIND11_SMART_HOLDER_ENABLED: - pytest.skip("smart_holder not available.", allow_module_level=True) - - -class PyBase(m.Base): # Avoiding name PyDerived, for more systematic naming. - def __init__(self): - m.Base.__init__(self) - - def get(self): - return 323 - - -class PyCppDerived(m.CppDerived): - def __init__(self): - m.CppDerived.__init__(self) - - def get(self): - return 434 - - -@pytest.mark.parametrize( - ("ctor", "expected"), - [ - (m.Base, 101), - (PyBase, 323), - (m.CppDerivedPlain, 202), - (m.CppDerived, 212), - (PyCppDerived, 434), - ], -) -def test_base_get(ctor, expected): - obj = ctor() - assert obj.get() == expected - - -@pytest.mark.parametrize( - ("ctor", "expected"), - [ - (m.Base, 4101), - (PyBase, 4323), - (m.CppDerivedPlain, 4202), - (m.CppDerived, 4212), - (PyCppDerived, 4434), - ], -) -def test_get_from_cpp_plainc_ptr(ctor, expected): - obj = ctor() - assert m.get_from_cpp_plainc_ptr(obj) == expected - - -@pytest.mark.parametrize( - ("ctor", "expected"), - [ - (m.Base, 5101), - (PyBase, 5323), - (m.CppDerivedPlain, 5202), - (m.CppDerived, 5212), - (PyCppDerived, 5434), - ], -) -def test_get_from_cpp_unique_ptr(ctor, expected): - obj = ctor() - assert m.get_from_cpp_unique_ptr(obj) == expected