diff --git a/include/pybind11/detail/init.h b/include/pybind11/detail/init.h index e21171688..0e9b302f8 100644 --- a/include/pybind11/detail/init.h +++ b/include/pybind11/detail/init.h @@ -38,6 +38,9 @@ PYBIND11_NAMESPACE_BEGIN(initimpl) inline void no_nullptr(void *ptr) { if (!ptr) { + if (PyErr_Occurred()) { + throw error_already_set(); + } throw type_error("pybind11::init(): factory function returned nullptr"); } } diff --git a/tests/test_factory_constructors.cpp b/tests/test_factory_constructors.cpp index a387cd2e7..d1ab93d10 100644 --- a/tests/test_factory_constructors.cpp +++ b/tests/test_factory_constructors.cpp @@ -412,6 +412,16 @@ TEST_SUBMODULE(factory_constructors, m) { "__init__", [](NoisyAlloc &a, int i, const std::string &) { new (&a) NoisyAlloc(i); }); }); + struct FactoryErrorAlreadySet {}; + py::class_(m, "FactoryErrorAlreadySet") + .def(py::init([](bool set_error) -> FactoryErrorAlreadySet * { + if (!set_error) { + return new FactoryErrorAlreadySet(); + } + py::set_error(PyExc_ValueError, "factory sets error and returns nullptr"); + return nullptr; + })); + // static_assert testing (the following def's should all fail with appropriate compilation // errors): #if 0 diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index a9004cbf6..1b326fdae 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -514,3 +514,10 @@ def test_invalid_self(): str(excinfo.value) == "__init__(self, ...) called with invalid or missing `self` argument" ) + + +def test_factory_error_already_set(): + obj = m.FactoryErrorAlreadySet(False) + assert isinstance(obj, m.FactoryErrorAlreadySet) + with pytest.raises(ValueError, match="factory sets error and returns nullptr"): + m.FactoryErrorAlreadySet(True)