There are three ways to handle an exception if it cannot be passed to the caller in the C code:
PyErr_WriteUnraisable()
/PyErr_FormatUnraisable()
. It was initially the most basic way. It writes “Exception ignored …” on the stderr and then writes the current exception name and message. No tracebacks, no notes, no exception chains. This is what you usually get if the exception is raised in the destructor or callback. Now it can be customizable by thesys.unraisablehook
hook.PyErr_Display()
/PyErr_DisplayException()
. It writes the specified exception in the way we used to to the stderr – with tracebacks, notes and recursively with__context__
and__cause__
chains.PyErr_Display()
can have a side effect of setting__traceback__
of the excedption. Now it is implemented in Python – imports and calls a function from thetraceback
module.PyErr_Print()
/PyErr_PrintEx()
. In interactive mode it prints the exit message and exits the program by callingPy_Exit()
if the current exception is SystemExit. Otherwise it can setsys.last_exc
and other variable and calls thesys.excepthook
hook which usesPyErr_Display()
by default.
Unfortunately, using these functions is not consistent across the extension code. I believe that PyErr_Print()
is completely unappropriate in the library code due to its side effects – possibility to exit the program and asynchronous changing the sys
attributes. It only should be used in the REPL and at the highest level of some programs. There may be some use for PyErr_Display()
, but it is not so handy and flexible. You need to explicitly write the context of the error to the stderr before writing the exception, and there is no way to catch the output except substitude the stderr.
PyErr_FormatUnraisable()
looks the most appropriate tool. It lacks traceback and other details, but importing from the traceback
module may be unappropriate – the import machinery may not work at that stage (too early or too late).