diff --git a/include/pybind11/detail/type_caster_base.h b/include/pybind11/detail/type_caster_base.h index ff80c4b0b..01312f14e 100644 --- a/include/pybind11/detail/type_caster_base.h +++ b/include/pybind11/detail/type_caster_base.h @@ -471,24 +471,22 @@ PYBIND11_NOINLINE bool isinstance_generic(handle obj, const std::type_info &tp) } PYBIND11_NOINLINE std::string -error_string(PyObject **exc_type, PyObject **exc_value, PyObject **exc_trace) { - if (*exc_type == nullptr) { +error_string(PyObject *exc_type, PyObject *exc_value, PyObject *exc_trace) { + if (exc_type == nullptr) { pybind11_fail( "Internal error: pybind11::detail::error_string() called with exc_type == nullptr"); } - PyErr_NormalizeException(exc_type, exc_value, exc_trace); - - auto result = handle(*exc_type).attr("__name__").cast(); + auto result = handle(exc_type).attr("__name__").cast(); result += ": "; - if (*exc_value) { - result += (std::string) str(*exc_value); + if (exc_value) { + result += (std::string) str(exc_value); } - if (*exc_trace) { + if (exc_trace) { #if !defined(PYPY_VERSION) - auto *tb = reinterpret_cast(*exc_trace); + auto *tb = reinterpret_cast(exc_trace); // Get the deepest trace possible. while (tb->tb_next) { @@ -531,7 +529,8 @@ error_string(PyObject **exc_type, PyObject **exc_value, PyObject **exc_trace) { PYBIND11_NOINLINE std::string error_string() { error_scope scope; // Fetch error state. - return error_string(&scope.type, &scope.value, &scope.trace); + PyErr_NormalizeException(&scope.type, &scope.value, &scope.trace); + return error_string(scope.type, scope.value, scope.trace); } PYBIND11_NOINLINE handle get_object_handle(const void *ptr, const detail::type_info *type) { diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 847ca34f3..dbd2c9fe1 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -364,7 +364,7 @@ T reinterpret_steal(handle h) { PYBIND11_NAMESPACE_BEGIN(detail) std::string error_string(); -std::string error_string(PyObject **, PyObject **, PyObject **); +std::string error_string(PyObject *, PyObject *, PyObject *); inline const char *obj_class_name(PyObject *obj) { if (Py_TYPE(obj) == &PyType_Type) { @@ -396,6 +396,7 @@ public: PyErr_SetString(PyExc_RuntimeError, m_lazy_what.c_str()); } PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); + PyErr_NormalizeException(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); } error_already_set(const error_already_set &) = default; @@ -409,7 +410,7 @@ public: const char *what() const noexcept override { if (m_lazy_what.empty()) { try { - m_lazy_what = detail::error_string(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); + m_lazy_what = detail::error_string(m_type.ptr(), m_value.ptr(), m_trace.ptr()); // Negate the if condition to test the catch(...) block below. if (m_lazy_what.empty()) { throw std::runtime_error( @@ -461,7 +462,8 @@ public: // if already set). void restore() { // As long as this type is copyable, there is no point in releasing m_type, m_value, - // m_trace. + // m_trace, but simply holding on the the references makes it possible to produce + // what() even after restore(). PyErr_Restore(m_type.inc_ref().ptr(), m_value.inc_ref().ptr(), m_trace.inc_ref().ptr()); }