diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index d34c92c24a..a66b8da05d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1406,6 +1406,9 @@ class class_ : public detail::generic_type { ); } v_h.value_ptr() = nullptr; + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(v_h.type ? (PyObject*)v_h.type->type : nullptr); + } } static detail::function_record *get_function_record(handle h) { diff --git a/tests/test_class.cpp b/tests/test_class.cpp index 5369cb064c..fdc1449394 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -390,6 +390,15 @@ TEST_SUBMODULE(class_, m) { py::class_(m, "PyPrintDestructor") .def(py::init<>()) .def("throw_something", &PyPrintDestructor::throw_something); + + struct PyRaiseDestructor { + PyRaiseDestructor() {} + ~PyRaiseDestructor() { + PyErr_SetString(PyExc_ValueError, "unraisable error"); + } + }; + py::class_(m, "PyRaiseDestructor") + .def(py::init<>()); } template class BreaksBase { public: diff --git a/tests/test_class.py b/tests/test_class.py index bbf8481a4d..5d81a6fbbc 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -329,3 +329,21 @@ class PyNonFinalFinalChild(m.IsNonFinalFinal): def test_exception_rvalue_abort(): with pytest.raises(RuntimeError): m.PyPrintDestructor().throw_something() + + +def test_pyexception_destructor(): + import sys + + if hasattr(sys, "unraisablehook"): + old_hook = sys.unraisablehook + try: + ctxt = [None] + + def new_hook(*args): + ctxt[0] = True + + sys.unraisablehook = new_hook + m.PyRaiseDestructor() + assert ctxt[0] is not None + finally: + sys.unraisablehook = old_hook