Dean Moldovan | 67b52d8 | 2016-10-16 19:12:43 +0200 | [diff] [blame] | 1 | Exceptions |
| 2 | ########## |
| 3 | |
| 4 | Built-in exception translation |
| 5 | ============================== |
| 6 | |
| 7 | When C++ code invoked from Python throws an ``std::exception``, it is |
| 8 | automatically converted into a Python ``Exception``. pybind11 defines multiple |
| 9 | special exception classes that will map to different types of Python |
| 10 | exceptions: |
| 11 | |
| 12 | .. tabularcolumns:: |p{0.5\textwidth}|p{0.45\textwidth}| |
| 13 | |
| 14 | +--------------------------------------+------------------------------+ |
| 15 | | C++ exception type | Python exception type | |
| 16 | +======================================+==============================+ |
| 17 | | :class:`std::exception` | ``RuntimeError`` | |
| 18 | +--------------------------------------+------------------------------+ |
| 19 | | :class:`std::bad_alloc` | ``MemoryError`` | |
| 20 | +--------------------------------------+------------------------------+ |
| 21 | | :class:`std::domain_error` | ``ValueError`` | |
| 22 | +--------------------------------------+------------------------------+ |
| 23 | | :class:`std::invalid_argument` | ``ValueError`` | |
| 24 | +--------------------------------------+------------------------------+ |
| 25 | | :class:`std::length_error` | ``ValueError`` | |
| 26 | +--------------------------------------+------------------------------+ |
| 27 | | :class:`std::out_of_range` | ``ValueError`` | |
| 28 | +--------------------------------------+------------------------------+ |
| 29 | | :class:`std::range_error` | ``ValueError`` | |
| 30 | +--------------------------------------+------------------------------+ |
| 31 | | :class:`pybind11::stop_iteration` | ``StopIteration`` (used to | |
| 32 | | | implement custom iterators) | |
| 33 | +--------------------------------------+------------------------------+ |
| 34 | | :class:`pybind11::index_error` | ``IndexError`` (used to | |
| 35 | | | indicate out of bounds | |
| 36 | | | accesses in ``__getitem__``, | |
| 37 | | | ``__setitem__``, etc.) | |
| 38 | +--------------------------------------+------------------------------+ |
| 39 | | :class:`pybind11::value_error` | ``ValueError`` (used to | |
| 40 | | | indicate wrong value passed | |
| 41 | | | in ``container.remove(...)`` | |
| 42 | +--------------------------------------+------------------------------+ |
| 43 | | :class:`pybind11::key_error` | ``KeyError`` (used to | |
| 44 | | | indicate out of bounds | |
| 45 | | | accesses in ``__getitem__``, | |
| 46 | | | ``__setitem__`` in dict-like | |
| 47 | | | objects, etc.) | |
| 48 | +--------------------------------------+------------------------------+ |
| 49 | | :class:`pybind11::error_already_set` | Indicates that the Python | |
| 50 | | | exception flag has already | |
| 51 | | | been initialized | |
| 52 | +--------------------------------------+------------------------------+ |
| 53 | |
| 54 | When a Python function invoked from C++ throws an exception, it is converted |
| 55 | into a C++ exception of type :class:`error_already_set` whose string payload |
| 56 | contains a textual summary. |
| 57 | |
| 58 | There is also a special exception :class:`cast_error` that is thrown by |
| 59 | :func:`handle::call` when the input arguments cannot be converted to Python |
| 60 | objects. |
| 61 | |
| 62 | Registering custom translators |
| 63 | ============================== |
| 64 | |
| 65 | If the default exception conversion policy described above is insufficient, |
| 66 | pybind11 also provides support for registering custom exception translators. |
| 67 | To register a simple exception conversion that translates a C++ exception into |
| 68 | a new Python exception using the C++ exception's ``what()`` method, a helper |
| 69 | function is available: |
| 70 | |
| 71 | .. code-block:: cpp |
| 72 | |
| 73 | py::register_exception<CppExp>(module, "PyExp"); |
| 74 | |
| 75 | This call creates a Python exception class with the name ``PyExp`` in the given |
| 76 | module and automatically converts any encountered exceptions of type ``CppExp`` |
| 77 | into Python exceptions of type ``PyExp``. |
| 78 | |
| 79 | When more advanced exception translation is needed, the function |
| 80 | ``py::register_exception_translator(translator)`` can be used to register |
| 81 | functions that can translate arbitrary exception types (and which may include |
| 82 | additional logic to do so). The function takes a stateless callable (e.g. a |
| 83 | function pointer or a lambda function without captured variables) with the call |
| 84 | signature ``void(std::exception_ptr)``. |
| 85 | |
| 86 | When a C++ exception is thrown, the registered exception translators are tried |
| 87 | in reverse order of registration (i.e. the last registered translator gets the |
| 88 | first shot at handling the exception). |
| 89 | |
| 90 | Inside the translator, ``std::rethrow_exception`` should be used within |
| 91 | a try block to re-throw the exception. One or more catch clauses to catch |
| 92 | the appropriate exceptions should then be used with each clause using |
| 93 | ``PyErr_SetString`` to set a Python exception or ``ex(string)`` to set |
| 94 | the python exception to a custom exception type (see below). |
| 95 | |
| 96 | To declare a custom Python exception type, declare a ``py::exception`` variable |
| 97 | and use this in the associated exception translator (note: it is often useful |
| 98 | to make this a static declaration when using it inside a lambda expression |
| 99 | without requiring capturing). |
| 100 | |
| 101 | |
| 102 | The following example demonstrates this for a hypothetical exception classes |
| 103 | ``MyCustomException`` and ``OtherException``: the first is translated to a |
| 104 | custom python exception ``MyCustomError``, while the second is translated to a |
| 105 | standard python RuntimeError: |
| 106 | |
| 107 | .. code-block:: cpp |
| 108 | |
| 109 | static py::exception<MyCustomException> exc(m, "MyCustomError"); |
| 110 | py::register_exception_translator([](std::exception_ptr p) { |
| 111 | try { |
| 112 | if (p) std::rethrow_exception(p); |
| 113 | } catch (const MyCustomException &e) { |
| 114 | exc(e.what()); |
| 115 | } catch (const OtherException &e) { |
| 116 | PyErr_SetString(PyExc_RuntimeError, e.what()); |
| 117 | } |
| 118 | }); |
| 119 | |
| 120 | Multiple exceptions can be handled by a single translator, as shown in the |
| 121 | example above. If the exception is not caught by the current translator, the |
| 122 | previously registered one gets a chance. |
| 123 | |
| 124 | If none of the registered exception translators is able to handle the |
| 125 | exception, it is handled by the default converter as described in the previous |
| 126 | section. |
| 127 | |
| 128 | .. seealso:: |
| 129 | |
| 130 | The file :file:`tests/test_exceptions.cpp` contains examples |
| 131 | of various custom exception translators and custom exception types. |
| 132 | |
| 133 | .. note:: |
| 134 | |
| 135 | You must call either ``PyErr_SetString`` or a custom exception's call |
| 136 | operator (``exc(string)``) for every exception caught in a custom exception |
| 137 | translator. Failure to do so will cause Python to crash with ``SystemError: |
| 138 | error return without exception set``. |
| 139 | |
| 140 | Exceptions that you do not plan to handle should simply not be caught, or |
| 141 | may be explicity (re-)thrown to delegate it to the other, |
| 142 | previously-declared existing exception translators. |