diff options
author | Vyas Ramasubramani <vyas.ramasubramani@gmail.com> | 2023-04-23 09:33:30 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-23 17:33:30 +0100 |
commit | 1183af2c99c62af93bf6fe89bc13a5772fb4f10b (patch) | |
tree | 7de0a8aa764165066b8ecf8aadf6989b4bc510dc | |
parent | bcb6c0e1af0086b7900db518e73e61e48ee5167b (diff) | |
download | cython-1183af2c99c62af93bf6fe89bc13a5772fb4f10b.tar.gz |
Add an example of a custom exception handler. (#5334)
* Add an example of a custom exception handler.
* Make sure to mention that any custom exception would work just as well
-rw-r--r-- | docs/src/userguide/wrapping_CPlusPlus.rst | 90 |
1 files changed, 88 insertions, 2 deletions
diff --git a/docs/src/userguide/wrapping_CPlusPlus.rst b/docs/src/userguide/wrapping_CPlusPlus.rst index b4df14e69..47b55245c 100644 --- a/docs/src/userguide/wrapping_CPlusPlus.rst +++ b/docs/src/userguide/wrapping_CPlusPlus.rst @@ -445,7 +445,10 @@ for Cython to discern that, so watch out with exception masks on IO streams. :: cdef int bar() except +MemoryError This will catch any C++ error and raise a Python MemoryError in its place. -(Any Python exception is valid here.) :: +(Any Python exception is valid here.) + +Cython also supports using a custom exception handler. This is an advanced feature +that most users won't need, but for those that do a full example follows:: cdef int raise_py_error() cdef int something_dangerous() except +raise_py_error @@ -453,7 +456,90 @@ This will catch any C++ error and raise a Python MemoryError in its place. If something_dangerous raises a C++ exception then raise_py_error will be called, which allows one to do custom C++ to Python error "translations." If raise_py_error does not actually raise an exception a RuntimeError will be -raised. +raised. This approach may also be used to manage custom Python exceptions +created using the Python C API. :: + + # raising.pxd + cdef extern from "Python.h" nogil: + ctypedef struct PyObject + + cdef extern from *: + """ + #include <Python.h> + #include <stdexcept> + #include <ios> + + PyObject *CustomLogicError; + + void create_custom_exceptions() { + CustomLogicError = PyErr_NewException("raiser.CustomLogicError", NULL, NULL); + } + + void custom_exception_handler() { + try { + if (PyErr_Occurred()) { + ; // let the latest Python exn pass through and ignore the current one + } else { + throw; + } + } catch (const std::logic_error& exn) { + // Add mapping of std::logic_error -> CustomLogicError + PyErr_SetString(CustomLogicError, exn.what()); + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Unknown exception"); + } + } + + class Raiser { + public: + Raiser () {} + void raise_exception() { + throw std::logic_error("Failure"); + } + }; + """ + cdef PyObject* CustomLogicError + cdef void create_custom_exceptions() + cdef void custom_exception_handler() + + cdef cppclass Raiser: + Raiser() noexcept + void raise_exception() except +custom_exception_handler + + + # raising.pyx + create_custom_exceptions() + PyCustomLogicError = <object> CustomLogicError + + + cdef class PyRaiser: + cdef Raiser c_obj + + def raise_exception(self): + self.c_obj.raise_exception() + +The above example leverages Cython's ability to include :ref:`verbatim C code +<verbatim_c>` in pxd files to create a new Python exception type +``CustomLogicError`` and map it to the standard C++ ``std::logic_error`` using +the ``custom_exception_handler`` function. There is nothing special about using +a standard exception class here, ``std::logic_error`` could easily be replaced +with some new C++ exception type defined in this file. The +``Raiser::raise_exception`` is marked with ``+custom_exception_handler`` to +indicate that this function should be called whenever an exception is raised. +The corresponding Python function ``PyRaiser.raise_exception`` will raise a +``CustomLogicError`` whenever it is called. Defining ``PyCustomLogicError`` +allows other code to catch this exception, as shown below: :: + + try: + PyRaiser().raise_exception() + except PyCustomLogicError: + print("Caught the exception") + +When defining custom exception handlers it is typically good to also include +logic to handle all the standard exceptions that Cython typically handles as +listed in the table above. The code for this standard exception handler can be +found `here +<https://github.com/cython/cython/blob/master/Cython/Utility/CppSupport.cpp>`__. There is also the special form:: |