-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Add support for nested exceptions in custom exceptions #4366
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
e9eb4ad
d068cc6
f51c638
beaaa1c
b2b6744
8984743
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 |
---|---|---|
|
@@ -314,16 +314,45 @@ inline internals **&get_internals_pp() { | |
return internals_pp; | ||
} | ||
|
||
// forward decl | ||
inline void translate_exception(std::exception_ptr); | ||
const std::forward_list<ExceptionTranslator> &get_exception_translators(); | ||
const std::forward_list<ExceptionTranslator> &get_local_exception_translators(); | ||
|
||
// Apply all the extensions translators from a list | ||
// Return true if one of the translators completed without raising an exception | ||
// itself. Return of false indicates that if there are other translators | ||
// available, they should be tried. | ||
inline bool apply_exception_translators(const std::forward_list<ExceptionTranslator> &translators, | ||
std::exception_ptr last_exception) { | ||
for (const auto &translator : translators) { | ||
try { | ||
translator(last_exception); | ||
return true; | ||
} catch (...) { | ||
last_exception = std::current_exception(); | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
inline bool | ||
apply_exception_translators(const std::forward_list<ExceptionTranslator> &translators) { | ||
return apply_exception_translators(translators, std::current_exception()); | ||
} | ||
|
||
template <class T, | ||
enable_if_t<std::is_same<std::nested_exception, remove_cvref_t<T>>::value, int> = 0> | ||
bool handle_nested_exception(const T &exc, const std::exception_ptr &p) { | ||
std::exception_ptr nested = exc.nested_ptr(); | ||
if (nested != nullptr && nested != p) { | ||
translate_exception(nested); | ||
return true; | ||
const auto &local_translators = get_local_exception_translators(); | ||
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. Do we need the temporary variables ( |
||
if (apply_exception_translators(local_translators, nested)) { | ||
return true; | ||
} | ||
|
||
const auto &translators = get_exception_translators(); | ||
if (apply_exception_translators(translators, nested)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
@@ -493,6 +522,10 @@ PYBIND11_NOINLINE internals &get_internals() { | |
return **internals_pp; | ||
} | ||
|
||
inline const std::forward_list<ExceptionTranslator> &get_exception_translators() { | ||
return get_internals().registered_exception_translators; | ||
} | ||
|
||
// the internals struct (above) is shared between all the modules. local_internals are only | ||
// for a single module. Any changes made to internals may require an update to | ||
// PYBIND11_INTERNALS_VERSION, breaking backwards compatibility. local_internals is, by design, | ||
|
@@ -549,6 +582,10 @@ inline local_internals &get_local_internals() { | |
return *locals; | ||
} | ||
|
||
inline const std::forward_list<ExceptionTranslator> &get_local_exception_translators() { | ||
return get_local_internals().registered_exception_translators; | ||
} | ||
|
||
/// Constructs a std::string with the given arguments, stores it in `internals`, and returns its | ||
/// `c_str()`. Such strings objects have a long storage duration -- the internal strings are only | ||
/// cleared when the program exits or after interpreter shutdown (when embedding), and so are | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,24 +52,6 @@ PYBIND11_WARNING_DISABLE_MSVC(4127) | |
|
||
PYBIND11_NAMESPACE_BEGIN(detail) | ||
|
||
// Apply all the extensions translators from a list | ||
// Return true if one of the translators completed without raising an exception | ||
// itself. Return of false indicates that if there are other translators | ||
// available, they should be tried. | ||
inline bool apply_exception_translators(std::forward_list<ExceptionTranslator> &translators) { | ||
auto last_exception = std::current_exception(); | ||
|
||
for (auto &translator : translators) { | ||
try { | ||
translator(last_exception); | ||
return true; | ||
} catch (...) { | ||
last_exception = std::current_exception(); | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
#if defined(_MSC_VER) | ||
# define PYBIND11_COMPAT_STRDUP _strdup | ||
#else | ||
|
@@ -2541,7 +2523,7 @@ class exception : public object { | |
} | ||
|
||
// Sets the current python exception to this exception object with the given message | ||
void operator()(const char *message) { PyErr_SetString(m_ptr, message); } | ||
void operator()(const char *message) { detail::raise_err(m_ptr, message); } | ||
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. Is this tested in this PR? This would allow for nested python exception that raised via raise from (ie. if the Python err indicator is already set) etc, 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. Yes, this is essentially tested by the existing unit tests for
with
|
||
}; | ||
|
||
PYBIND11_NAMESPACE_BEGIN(detail) | ||
|
@@ -2573,6 +2555,8 @@ register_exception_impl(handle scope, const char *name, handle base, bool isLoca | |
try { | ||
std::rethrow_exception(p); | ||
} catch (const CppException &e) { | ||
detail::handle_nested_exception(e, p); | ||
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. This wouldn't properly handle nested custom / CppExceptions, properly though, right? Only would work if it's an STL exception, not if it's a nested CppException? We may want to add a test case where it breaks. It's unclear here which exception translator the nested exception should use, but it seems a bit bugprone to use the default one, no? 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. Thanks for the catch! I completely oversaw that Although I am an avid user of pybind11, I have very little experience with its code base. In fact I started fiddling with it just a few hours ago. So please let me know if anything is obviously wrong or needs a different approach. |
||
|
||
detail::get_exception_object<CppException>()(e.what()); | ||
} | ||
}); | ||
|
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.
Is
extensions
correct here? I see this comment is just moved from pybind11.h, but maybe it's a long-standing oversight?