Skip to content

Commit bef0832

Browse files
committed
Update documentation for new local exception feature
1 parent 696ece0 commit bef0832

File tree

1 file changed

+68
-7
lines changed

1 file changed

+68
-7
lines changed

docs/advanced/exceptions.rst

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ Registering custom translators
7575

7676
If the default exception conversion policy described above is insufficient,
7777
pybind11 also provides support for registering custom exception translators.
78-
To register a simple exception conversion that translates a C++ exception into
79-
a new Python exception using the C++ exception's ``what()`` method, a helper
80-
function is available:
78+
Similar to pybind11 classes, exception translators can be local to the module
79+
they are defined in or global to the entire python session. To register a simple
80+
exception conversion that translates a C++ exception into a new Python exception
81+
using the C++ exception's ``what()`` method, a helper function is available:
8182

8283
.. code-block:: cpp
8384
@@ -87,29 +88,39 @@ This call creates a Python exception class with the name ``PyExp`` in the given
8788
module and automatically converts any encountered exceptions of type ``CppExp``
8889
into Python exceptions of type ``PyExp``.
8990

91+
A matching function is available for registering a local exception translator:
92+
93+
.. code-block:: cpp
94+
95+
py::register_local_exception<CppExp>(module, "PyExp");
96+
97+
9098
It is possible to specify base class for the exception using the third
9199
parameter, a `handle`:
92100

93101
.. code-block:: cpp
94102
95103
py::register_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
104+
py::register_local_exception<CppExp>(module, "PyExp", PyExc_RuntimeError);
96105
97106
Then `PyExp` can be caught both as `PyExp` and `RuntimeError`.
98107

99108
The class objects of the built-in Python exceptions are listed in the Python
100109
documentation on `Standard Exceptions <https://docs.python.org/3/c-api/exceptions.html#standard-exceptions>`_.
101110
The default base class is `PyExc_Exception`.
102111

103-
When more advanced exception translation is needed, the function
104-
``py::register_exception_translator(translator)`` can be used to register
112+
When more advanced exception translation is needed, the functions
113+
``py::register_exception_translator(translator)`` and
114+
``py::register_local_exception_translator(translator)`` can be used to register
105115
functions that can translate arbitrary exception types (and which may include
106-
additional logic to do so). The function takes a stateless callable (e.g. a
116+
additional logic to do so). The functions takes a stateless callable (e.g. a
107117
function pointer or a lambda function without captured variables) with the call
108118
signature ``void(std::exception_ptr)``.
109119

110120
When a C++ exception is thrown, the registered exception translators are tried
111121
in reverse order of registration (i.e. the last registered translator gets the
112-
first shot at handling the exception).
122+
first shot at handling the exception). All local translators will be tried
123+
before a global translator is tried.
113124

114125
Inside the translator, ``std::rethrow_exception`` should be used within
115126
a try block to re-throw the exception. One or more catch clauses to catch
@@ -168,6 +179,56 @@ section.
168179
with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``.
169180
See also: "Problems with C++ exceptions" under `GCC Wiki <https://gcc.gnu.org/wiki/Visibility>`_.
170181

182+
183+
Local vs Global Exception Translators
184+
=====================================
185+
186+
Similar to how the ``py::module_local`` flag allows uesrs to limit a bound class
187+
to a single compiled module to prevent name collisions. When a global exception
188+
translator is registered, it will be applied across all modules in the reverse
189+
order of registration. This can create behavior where the order of module import
190+
influences how exceptions are translated.
191+
192+
If module1 has the following translator:
193+
194+
.. code-block:: cpp
195+
196+
py::register_exception_translator([](std::exception_ptr p) {
197+
try {
198+
if (p) std::rethrow_exception(p);
199+
} catch (const std::invalid_argument &e) {
200+
PyErr_SetString("module1 handled this")
201+
}
202+
}
203+
204+
and module2 has the following similar translator:
205+
206+
.. code-block:: cpp
207+
208+
py::register_exception_translator([](std::exception_ptr p) {
209+
try {
210+
if (p) std::rethrow_exception(p);
211+
} catch (const std::invalid_argument &e) {
212+
PyErr_SetString("module2 handled this")
213+
}
214+
}
215+
216+
then which translator handles the invalid_argument will be determined by the
217+
order that module1 and module2 are imported. Since exception translators are
218+
applied in the reverse order of registration, which ever module was imported
219+
last will "win" and that translator will be applied.
220+
221+
If there are multiple pybind11 modules that share exception types (either
222+
standard built-in or custom) loaded into a single python instance and
223+
consistent error handling behavior is needed, then local translators should be
224+
used.
225+
226+
Changing the previous example to use register_local_exception_translator would
227+
mean that when invalid_argument is thrown in the module2 code, the module2
228+
translator will always handle it, while in module1, the module1 translator will
229+
do the same.
230+
>>>>>>> 854aa95a (Update documentation for new local exception feature)
231+
171232
.. _handling_python_exceptions_cpp:
172233

173234
Handling exceptions from Python in C++

0 commit comments

Comments
 (0)