Skip to main content

Overview

Exception handling in C extensions requires explicit checking of return values and managing the exception state.

Checking for Exceptions

PyObject* PyErr_Occurred(void)
Check if an exception is set. Returns: Exception type, or NULL if no exception

Setting Exceptions

PyErr_SetString

void PyErr_SetString(PyObject *type, const char *message)
Example:
if (value < 0) {
    PyErr_SetString(PyExc_ValueError, "Value must be non-negative");
    return NULL;
}

PyErr_Format

PyObject* PyErr_Format(PyObject *exception, const char *format, ...)
Example:
if (index >= size) {
    PyErr_Format(PyExc_IndexError,
                 "Index %d out of range (size=%d)",
                 index, size);
    return NULL;
}

Standard Exceptions

PyExc_Exception
PyExc_TypeError
PyExc_ValueError
PyExc_IndexError
PyExc_KeyError
PyExc_MemoryError
PyExc_RuntimeError
PyExc_IOError
PyExc_AttributeError

Clearing Exceptions

void PyErr_Clear(void)
Clear the exception state. Example:
PyObject *item = PyDict_GetItem(dict, key);
if (item == NULL && PyErr_Occurred()) {
    if (PyErr_ExceptionMatches(PyExc_KeyError)) {
        PyErr_Clear();
        // Handle missing key
        item = default_value;
    } else {
        return NULL;  // Propagate other errors
    }
}

Exception Matching

int PyErr_ExceptionMatches(PyObject *exc)
Check if current exception matches type.

Printing Exceptions

void PyErr_Print(void)
void PyErr_PrintEx(int set_sys_last_vars)
Print exception traceback to stderr.

Custom Exceptions

PyObject* PyErr_NewException(const char *name,
                             PyObject *base,
                             PyObject *dict)
Example:
static PyObject *SpamError;

PyMODINIT_FUNC PyInit_spam(void) {
    PyObject *m = PyModule_Create(&spammodule);
    if (m == NULL)
        return NULL;
    
    SpamError = PyErr_NewException("spam.error", NULL, NULL);
    Py_XINCREF(SpamError);
    if (PyModule_AddObject(m, "error", SpamError) < 0) {
        Py_XDECREF(SpamError);
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }
    
    return m;
}

Error Handling Pattern

static PyObject* risky_function(PyObject *self, PyObject *args) {
    PyObject *a = NULL, *b = NULL, *result = NULL;
    
    a = PyList_New(0);
    if (a == NULL)
        goto error;
    
    b = PyDict_New();
    if (b == NULL)
        goto error;
    
    // Perform operations...
    result = process(a, b);
    if (result == NULL)
        goto error;
    
    Py_DECREF(a);
    Py_DECREF(b);
    return result;
    
error:
    Py_XDECREF(a);
    Py_XDECREF(b);
    return NULL;
}

See Also

Build docs developers (and LLMs) love