diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a6ac3a0d7..b79e755e4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -125,6 +125,7 @@ set(PYBIND11_TEST_FILES test_class_sh_factory_constructors test_class_sh_inheritance test_class_sh_mi_thunks + test_class_sh_module_local.py test_class_sh_property test_class_sh_property_non_owning test_class_sh_shared_ptr_copy_move @@ -238,6 +239,8 @@ tests_extra_targets("test_exceptions.py;test_local_bindings.py;test_stl.py;test_ # And add additional targets for other tests. tests_extra_targets("test_exceptions.py" "cross_module_interleaved_error_already_set") tests_extra_targets("test_gil_scoped.py" "cross_module_gil_utils") +tests_extra_targets("test_class_sh_module_local.py" + "class_sh_module_local_0;class_sh_module_local_1;class_sh_module_local_2") set(PYBIND11_EIGEN_REPO "https://gitlab.com/libeigen/eigen.git" diff --git a/tests/class_sh_module_local_0.cpp b/tests/class_sh_module_local_0.cpp new file mode 100644 index 000000000..bb1f46d5c --- /dev/null +++ b/tests/class_sh_module_local_0.cpp @@ -0,0 +1,27 @@ +#include + +#include + +namespace pybind11_tests { +namespace class_sh_module_local { + +struct atyp { // Short for "any type". + std::string mtxt; +}; + +std::string get_mtxt(const atyp &obj) { return obj.mtxt; } + +atyp rtrn_valu_atyp() { return atyp(); } + +} // namespace class_sh_module_local +} // namespace pybind11_tests + +PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp) + +PYBIND11_MODULE(class_sh_module_local_0, m) { + using namespace pybind11_tests::class_sh_module_local; + + m.def("get_mtxt", get_mtxt); + + m.def("rtrn_valu_atyp", rtrn_valu_atyp); +} diff --git a/tests/class_sh_module_local_1.cpp b/tests/class_sh_module_local_1.cpp new file mode 100644 index 000000000..66bc95516 --- /dev/null +++ b/tests/class_sh_module_local_1.cpp @@ -0,0 +1,33 @@ +// Identical to class_sh_module_local_2.cpp, except 2 replaced with 1. +#include + +#include + +namespace pybind11_tests { +namespace class_sh_module_local { + +struct atyp { // Short for "any type". + std::string mtxt; +}; + +std::string get_mtxt(const atyp &obj) { return obj.mtxt; } + +} // namespace class_sh_module_local +} // namespace pybind11_tests + +PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp) + +PYBIND11_MODULE(class_sh_module_local_1, m) { + namespace py = pybind11; + using namespace pybind11_tests::class_sh_module_local; + + py::classh(m, "atyp", py::module_local()) + .def(py::init([](const std::string &mtxt) { + atyp obj; + obj.mtxt = mtxt; + return obj; + })) + .def("tag", [](const atyp &) { return 1; }); + + m.def("get_mtxt", get_mtxt); +} diff --git a/tests/class_sh_module_local_2.cpp b/tests/class_sh_module_local_2.cpp new file mode 100644 index 000000000..bef105aad --- /dev/null +++ b/tests/class_sh_module_local_2.cpp @@ -0,0 +1,33 @@ +// Identical to class_sh_module_local_1.cpp, except 1 replaced with 2. +#include + +#include + +namespace pybind11_tests { +namespace class_sh_module_local { + +struct atyp { // Short for "any type". + std::string mtxt; +}; + +std::string get_mtxt(const atyp &obj) { return obj.mtxt; } + +} // namespace class_sh_module_local +} // namespace pybind11_tests + +PYBIND11_SMART_HOLDER_TYPE_CASTERS(pybind11_tests::class_sh_module_local::atyp) + +PYBIND11_MODULE(class_sh_module_local_2, m) { + namespace py = pybind11; + using namespace pybind11_tests::class_sh_module_local; + + py::classh(m, "atyp", py::module_local()) + .def(py::init([](const std::string &mtxt) { + atyp obj; + obj.mtxt = mtxt; + return obj; + })) + .def("tag", [](const atyp &) { return 2; }); + + m.def("get_mtxt", get_mtxt); +} diff --git a/tests/test_class_sh_module_local.py b/tests/test_class_sh_module_local.py new file mode 100644 index 000000000..e504152a1 --- /dev/null +++ b/tests/test_class_sh_module_local.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +import class_sh_module_local_0 as m0 +import class_sh_module_local_1 as m1 +import class_sh_module_local_2 as m2 +import pytest + + +def test_cross_module_get_mtxt(): + obj1 = m1.atyp("A") + assert obj1.tag() == 1 + obj2 = m2.atyp("B") + assert obj2.tag() == 2 + assert m1.get_mtxt(obj1) == "A" + assert m2.get_mtxt(obj2) == "B" + assert m1.get_mtxt(obj2) == "B" + assert m2.get_mtxt(obj1) == "A" + assert m0.get_mtxt(obj1) == "A" + assert m0.get_mtxt(obj2) == "B" + + +def test_m0_rtrn_valu_atyp(): + with pytest.raises(TypeError) as exc_info: + m0.rtrn_valu_atyp() + assert str(exc_info.value).startswith( + "Unable to convert function return value to a Python type!" + )