Blame external/pybind11/docs/advanced/exceptions.rst

Packit 534379
Exceptions
Packit 534379
##########
Packit 534379
Packit 534379
Built-in exception translation
Packit 534379
==============================
Packit 534379
Packit 534379
When C++ code invoked from Python throws an ``std::exception``, it is
Packit 534379
automatically converted into a Python ``Exception``. pybind11 defines multiple
Packit 534379
special exception classes that will map to different types of Python
Packit 534379
exceptions:
Packit 534379
Packit 534379
.. tabularcolumns:: |p{0.5\textwidth}|p{0.45\textwidth}|
Packit 534379
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
|  C++ exception type                  |  Python exception type               |
Packit 534379
+======================================+======================================+
Packit 534379
| :class:`std::exception`              | ``RuntimeError``                     |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`std::bad_alloc`              | ``MemoryError``                      |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`std::domain_error`           | ``ValueError``                       |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`std::invalid_argument`       | ``ValueError``                       |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`std::length_error`           | ``ValueError``                       |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`std::out_of_range`           | ``IndexError``                       |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`std::range_error`            | ``ValueError``                       |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`pybind11::stop_iteration`    | ``StopIteration`` (used to implement |
Packit 534379
|                                      | custom iterators)                    |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`pybind11::index_error`       | ``IndexError`` (used to indicate out |
Packit 534379
|                                      | of bounds access in ``__getitem__``, |
Packit 534379
|                                      | ``__setitem__``, etc.)               |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`pybind11::value_error`       | ``ValueError`` (used to indicate     |
Packit 534379
|                                      | wrong value passed in                |
Packit 534379
|                                      | ``container.remove(...)``)           |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`pybind11::key_error`         | ``KeyError`` (used to indicate out   |
Packit 534379
|                                      | of bounds access in ``__getitem__``, |
Packit 534379
|                                      | ``__setitem__`` in dict-like         |
Packit 534379
|                                      | objects, etc.)                       |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
| :class:`pybind11::error_already_set` | Indicates that the Python exception  |
Packit 534379
|                                      | flag has already been set via Python |
Packit 534379
|                                      | API calls from C++ code; this C++    |
Packit 534379
|                                      | exception is used to propagate such  |
Packit 534379
|                                      | a Python exception back to Python.   |
Packit 534379
+--------------------------------------+--------------------------------------+
Packit 534379
Packit 534379
When a Python function invoked from C++ throws an exception, it is converted
Packit 534379
into a C++ exception of type :class:`error_already_set` whose string payload
Packit 534379
contains a textual summary.
Packit 534379
Packit 534379
There is also a special exception :class:`cast_error` that is thrown by
Packit 534379
:func:`handle::call` when the input arguments cannot be converted to Python
Packit 534379
objects.
Packit 534379
Packit 534379
Registering custom translators
Packit 534379
==============================
Packit 534379
Packit 534379
If the default exception conversion policy described above is insufficient,
Packit 534379
pybind11 also provides support for registering custom exception translators.
Packit 534379
To register a simple exception conversion that translates a C++ exception into
Packit 534379
a new Python exception using the C++ exception's ``what()`` method, a helper
Packit 534379
function is available:
Packit 534379
Packit 534379
.. code-block:: cpp
Packit 534379
Packit 534379
    py::register_exception<CppExp>(module, "PyExp");
Packit 534379
Packit 534379
This call creates a Python exception class with the name ``PyExp`` in the given
Packit 534379
module and automatically converts any encountered exceptions of type ``CppExp``
Packit 534379
into Python exceptions of type ``PyExp``.
Packit 534379
Packit 534379
When more advanced exception translation is needed, the function
Packit 534379
``py::register_exception_translator(translator)`` can be used to register
Packit 534379
functions that can translate arbitrary exception types (and which may include
Packit 534379
additional logic to do so).  The function takes a stateless callable (e.g.  a
Packit 534379
function pointer or a lambda function without captured variables) with the call
Packit 534379
signature ``void(std::exception_ptr)``.
Packit 534379
Packit 534379
When a C++ exception is thrown, the registered exception translators are tried
Packit 534379
in reverse order of registration (i.e. the last registered translator gets the
Packit 534379
first shot at handling the exception).
Packit 534379
Packit 534379
Inside the translator, ``std::rethrow_exception`` should be used within
Packit 534379
a try block to re-throw the exception.  One or more catch clauses to catch
Packit 534379
the appropriate exceptions should then be used with each clause using
Packit 534379
``PyErr_SetString`` to set a Python exception or ``ex(string)`` to set
Packit 534379
the python exception to a custom exception type (see below).
Packit 534379
Packit 534379
To declare a custom Python exception type, declare a ``py::exception`` variable
Packit 534379
and use this in the associated exception translator (note: it is often useful
Packit 534379
to make this a static declaration when using it inside a lambda expression
Packit 534379
without requiring capturing).
Packit 534379
Packit 534379
Packit 534379
The following example demonstrates this for a hypothetical exception classes
Packit 534379
``MyCustomException`` and ``OtherException``: the first is translated to a
Packit 534379
custom python exception ``MyCustomError``, while the second is translated to a
Packit 534379
standard python RuntimeError:
Packit 534379
Packit 534379
.. code-block:: cpp
Packit 534379
Packit 534379
    static py::exception<MyCustomException> exc(m, "MyCustomError");
Packit 534379
    py::register_exception_translator([](std::exception_ptr p) {
Packit 534379
        try {
Packit 534379
            if (p) std::rethrow_exception(p);
Packit 534379
        } catch (const MyCustomException &e) {
Packit 534379
            exc(e.what());
Packit 534379
        } catch (const OtherException &e) {
Packit 534379
            PyErr_SetString(PyExc_RuntimeError, e.what());
Packit 534379
        }
Packit 534379
    });
Packit 534379
Packit 534379
Multiple exceptions can be handled by a single translator, as shown in the
Packit 534379
example above. If the exception is not caught by the current translator, the
Packit 534379
previously registered one gets a chance.
Packit 534379
Packit 534379
If none of the registered exception translators is able to handle the
Packit 534379
exception, it is handled by the default converter as described in the previous
Packit 534379
section.
Packit 534379
Packit 534379
.. seealso::
Packit 534379
Packit 534379
    The file :file:`tests/test_exceptions.cpp` contains examples
Packit 534379
    of various custom exception translators and custom exception types.
Packit 534379
Packit 534379
.. note::
Packit 534379
Packit 534379
    You must call either ``PyErr_SetString`` or a custom exception's call
Packit 534379
    operator (``exc(string)``) for every exception caught in a custom exception
Packit 534379
    translator.  Failure to do so will cause Python to crash with ``SystemError:
Packit 534379
    error return without exception set``.
Packit 534379
Packit 534379
    Exceptions that you do not plan to handle should simply not be caught, or
Packit 534379
    may be explicitly (re-)thrown to delegate it to the other,
Packit 534379
    previously-declared existing exception translators.