Skip to main content

Python/errors.c (part 7)

Source:

cpython 3.14 @ ab2d84fe1023/Python/errors.c

This annotation covers error formatting and unraisable exception handling. See python_errors6_detail for PyErr_SetObject, PyErr_Occurred, _PyErr_StackItem, and exception chaining.

Map

LinesSymbolRole
1-80_PyErr_FormatVvprintf-style error message construction
81-180PyErr_FormatVarargs wrapper around _PyErr_FormatV
181-300PyErr_FormatUnraisableFormat and print an unraisable exception
301-420_PyErr_WriteUnraisableMsgLow-level unraisable writer, calls sys.unraisablehook
421-500_PyBaseExceptionObject_fetch_tracebackExtract traceback for chained display

Reading

_PyErr_FormatV

// CPython: Python/errors.c:1020 _PyErr_FormatV
PyObject *
_PyErr_FormatV(PyThreadState *tstate, PyObject *exception,
const char *format, va_list vargs)
{
PyObject *string = PyUnicode_FromFormatV(format, vargs);
_PyErr_SetObject(tstate, exception, string);
Py_XDECREF(string);
return NULL;
}

_PyErr_FormatV always returns NULL so callers can write return PyErr_Format(exc, "..."). The format string uses %S for PyObject * (calls str()), %R for repr(), %A for ascii(), %T for type(obj).__name__.

PyErr_Format

// CPython: Python/errors.c:1045 PyErr_Format
PyObject *
PyErr_Format(PyObject *exception, const char *format, ...)
{
va_list vargs;
va_start(vargs, format);
_PyErr_FormatV(tstate, exception, format, vargs);
va_end(vargs);
return NULL;
}

PyErr_Format is the most common way to raise exceptions in C extensions. The %T specifier (type name) was added in CPython 3.12; older code used Py_TYPE(obj)->tp_name.

PyErr_FormatUnraisable

// CPython: Python/errors.c:1120 PyErr_FormatUnraisable
void
PyErr_FormatUnraisable(const char *format, ...)
{
/* Build an object string from format args, then call
_PyErr_WriteUnraisableMsg with the current exception. */
va_list vargs;
va_start(vargs, format);
PyObject *msg = PyUnicode_FromFormatV(format, vargs);
va_end(vargs);
_PyErr_WriteUnraisableMsg(PyUnicode_AsUTF8(msg), NULL);
Py_XDECREF(msg);
}

PyErr_FormatUnraisable is used in finalizers (__del__, __exit__ called during GC) where raising would be lost. The message is printed to sys.stderr via sys.unraisablehook.

_PyErr_WriteUnraisableMsg

// CPython: Python/errors.c:1180 _PyErr_WriteUnraisableMsg
void
_PyErr_WriteUnraisableMsg(const char *err_msg, PyObject *obj)
{
PyObject *exc = PyErr_GetRaisedException();
if (exc == NULL) return;
/* Build an UnraisableHookArgs namedtuple and call sys.unraisablehook */
PyObject *hook_args = make_unraisable_hook_args(exc, obj, err_msg);
PyObject *hook = PySys_GetObject("unraisablehook");
if (hook != NULL) {
PyObject *res = PyObject_CallOneArg(hook, hook_args);
Py_XDECREF(res);
}
/* Fallback: write directly to sys.stderr */
if (_PyErr_Occurred(tstate)) {
_PyErr_Clear(tstate);
PyObject_CallMethodObjArgs(sys_stderr, &_Py_ID(write), ...);
}
Py_DECREF(exc);
}

sys.unraisablehook (added in 3.8) receives an UnraisableHookArgs(exc_type, exc_value, exc_traceback, err_msg, object). The default hook writes to sys.stderr. Override it to route unraisable exceptions to a logging system.

Chained exception display

// CPython: Python/errors.c:1320 _PyBaseExceptionObject_fetch_traceback
/* When printing a traceback, check:
__cause__ -> "The above exception was the direct cause of..."
__context__ -> "During handling of the above exception, another..."
__suppress_context__ -> set by "raise X from None"
*/

The __cause__ / __context__ chain is walked recursively in traceback.py; this C function just exposes __traceback__ for the C traceback printer used at interpreter shutdown.

gopy notes

_PyErr_FormatV is errors.FormatV in objects/errors.go. PyErr_FormatUnraisable is errors.FormatUnraisable. _PyErr_WriteUnraisableMsg calls vm.CallUnraisableHook in vm/eval_unwind.go. The UnraisableHookArgs namedtuple is objects.UnraisableHookArgs.