mirror of
https://github.com/pybind/pybind11.git
synced 2026-04-20 14:59:27 +00:00
Add and document py::error_already_set::discard_as_unraisable()
To deal with exceptions that hit destructors or other noexcept functions. Includes fixes to support Python 2.7 and extends documentation on error handling. @virtuald and @YannickJadoul both contributed to this PR.
This commit is contained in:
committed by
Ralf W. Grosse-Kunstleve
parent
a876aac2cf
commit
3618bea2aa
@@ -53,9 +53,15 @@ exceptions:
|
||||
| | a Python exception back to Python. |
|
||||
+--------------------------------------+--------------------------------------+
|
||||
|
||||
When a Python function invoked from C++ throws an exception, it is converted
|
||||
into a C++ exception of type :class:`error_already_set` whose string payload
|
||||
contains a textual summary.
|
||||
When a Python function invoked from C++ throws an exception, pybind11 will convert
|
||||
it into a C++ exception of type :class:`error_already_set` whose string payload
|
||||
contains a textual summary. If you call the Python C-API directly, and it
|
||||
returns an error, you should ``throw py::error_already_set();``, which allows
|
||||
pybind11 to deal with the exception and pass it back to the Python interpreter.
|
||||
(Another option is to call ``PyErr_Clear`` in the
|
||||
`Python C-API <https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Clear>`_
|
||||
to clear the error. The Python error must be thrown or cleared, or Python/pybind11
|
||||
will be left in an invalid state.)
|
||||
|
||||
There is also a special exception :class:`cast_error` that is thrown by
|
||||
:func:`handle::call` when the input arguments cannot be converted to Python
|
||||
@@ -142,3 +148,43 @@ section.
|
||||
Exceptions that you do not plan to handle should simply not be caught, or
|
||||
may be explicitly (re-)thrown to delegate it to the other,
|
||||
previously-declared existing exception translators.
|
||||
|
||||
.. _unraisable_exceptions:
|
||||
|
||||
Handling unraisable exceptions
|
||||
==============================
|
||||
|
||||
If a Python function invoked from a C++ destructor or any function marked
|
||||
``noexcept(true)`` (collectively, "noexcept functions") throws an exception, there
|
||||
is no way to propagate the exception, as such functions may not throw at
|
||||
run-time.
|
||||
|
||||
Neither Python nor C++ allow exceptions raised in a noexcept function to propagate. In
|
||||
Python, an exception raised in a class's ``__del__`` method is logged as an
|
||||
unraisable error. In Python 3.8+, a system hook is triggered and an auditing
|
||||
event is logged. In C++, ``std::terminate()`` is called to abort immediately.
|
||||
|
||||
Any noexcept function should have a try-catch block that traps
|
||||
class:`error_already_set` (or any other exception that can occur). Note that pybind11
|
||||
wrappers around Python exceptions such as :class:`pybind11::value_error` are *not*
|
||||
Python exceptions; they are C++ exceptions that pybind11 catches and converts to
|
||||
Python exceptions. Noexcept functions cannot propagate these exceptions either.
|
||||
You can convert them to Python exceptions and then discard as unraisable.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void nonthrowing_func() noexcept(true) {
|
||||
try {
|
||||
// ...
|
||||
} catch (py::error_already_set &eas) {
|
||||
// Discard the Python error using Python APIs, using the C++ magic
|
||||
// variable __func__. Python already knows the type and value and of the
|
||||
// exception object.
|
||||
eas.discard_as_unraisable(__func__);
|
||||
} catch (const std::exception &e) {
|
||||
// Log and discard C++ exceptions.
|
||||
// (We cannot use discard_as_unraisable, since we have a generic C++
|
||||
// exception, not an exception that originated from Python.)
|
||||
third_party::log(e);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user