Expose exception cause as std::nested_exception

This commit is contained in:
Ed Catmur 2022-06-28 15:12:00 +01:00
parent dd3bf7fd12
commit 1bd0eaae06
1 changed files with 20 additions and 2 deletions

View File

@ -546,6 +546,8 @@ struct error_fetch_and_normalize {
return (PyErr_GivenExceptionMatches(m_type.ptr(), exc.ptr()) != 0);
}
std::nested_exception get_cause_as_nested() const;
// Not protecting these for simplicity.
object m_type, m_value, m_trace;
@ -572,13 +574,15 @@ PYBIND11_NAMESPACE_END(detail)
/// thrown to propagate python-side errors back through C++ which can either be caught manually or
/// else falls back to the function dispatcher (which then raises the captured error back to
/// python).
class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception {
class PYBIND11_EXPORT_EXCEPTION error_already_set : public std::exception, public std::nested_exception {
public:
/// Fetches the current Python exception (using PyErr_Fetch()), which will clear the
/// current Python error indicator.
error_already_set()
: m_fetched_error{new detail::error_fetch_and_normalize("pybind11::error_already_set"),
m_fetched_error_deleter} {}
m_fetched_error_deleter} {
static_cast<std::nested_exception&>(*this) = m_fetched_error->get_cause_as_nested();
}
/// The what() result is built lazily on demand.
/// WARNING: This member function needs to acquire the Python GIL. This can lead to
@ -2327,6 +2331,20 @@ bool object_api<D>::rich_compare(object_api const &other, int value) const {
return rv == 1;
}
inline std::nested_exception error_fetch_and_normalize::get_cause_as_nested() const {
auto cause = reinterpret_steal<object>(PyException_GetCause(m_value.ptr()));
if (not cause or cause.is_none())
return std::nested_exception();
auto typ = type::of(cause);
auto tb = reinterpret_steal<object>(PyException_GetTraceback(cause.ptr()));
PyErr_Restore(typ.release().ptr(), cause.release().ptr(), tb.release().ptr());
try {
throw error_already_set();
} catch (...) {
return std::nested_exception();
}
}
#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \
template <typename D> \
object object_api<D>::op() const { \