-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Expose exception cause as std::nested_exception #4033
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,17 @@ 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} { | ||
if (not nested_ptr()) | ||
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 +2333,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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Throwing is very slow and something that is best avoided here. is there a way we avoid having to throw the exception just to catch and return an empty std::nested_exception? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we just use the CPython API to check if the exception is nested instead? |
||
} catch (...) { | ||
return std::nested_exception(); | ||
} | ||
} | ||
|
||
#define PYBIND11_MATH_OPERATOR_UNARY(op, fn) \ | ||
template <typename D> \ | ||
object object_api<D>::op() const { \ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PyException_GetTraceback can return null. Will PyErr_Restore work if this is the case?