Raise Python Exception from a C Extension



When writing a C extension for Python, you might need to raise an exception if something goes wrong in your C code. Python provides a special C API that helps you to raise errors in a way that works just like regular Python exceptions.

This is helpful because even if your code is written in C for better performance, users can still handle errors using Python's normal try...except blocks. It makes your extension feel just like any other Python module.

Using Python C API to Raise Exceptions

In C extensions for Python, you can raise exceptions using the Python C API functions like PyErr_SetString() or PyErr_SetObject(). These functions don't stop the C code right away, they just tell Python that an error has happened.

To actually raise the exception in Python, your C function must return NULL. This signals the Python interpreter that an error occurred, and it will then handle the exception as if it was raised in normal Python code.

Example

The following C extension code defines a function that raises a ValueError if the input number is negative -


#include <Python.h>

static PyObject* check_positive(PyObject* self, PyObject* args) {
int num;
if (!PyArg_ParseTuple(args, "i", &num))
return NULL;

if (num < 0) {
PyErr_SetString(PyExc_ValueError, "Negative value not allowed");
return NULL;
}

return PyLong_FromLong(num);
}

static PyMethodDef MyMethods[] = {
{"check_positive", check_positive, METH_VARARGS, "Checks if a number is positive"},
{NULL, NULL, 0, NULL}
};

static struct PyModuleDef mymodule = {
PyModuleDef_HEAD_INIT,
"myextension", /* name of module */
NULL, /* module documentation */
-1, /* size of per-interpreter state of the module */
MyMethods
};

PyMODINIT_FUNC PyInit_myextension(void) {
return PyModule_Create(&mymodule);
}

After compiling the extension, you can use it in Python like this -

import myextension

myextension.check_positive(10) # Returns 10
myextension.check_positive(-5) # Raises ValueError

We get the following error for the second call -

ValueError: Negative value not allowed

This shows how you can raise a Python exception from C and handle it just like any normal Python error.

Raising Different Types of Exceptions

You can raise any built-in or custom exception from your Python C extension using the appropriate exception object. Python provides many built-in types like PyExc_TypeError, PyExc_ValueError, PyExc_IndexError, PyExc_IOError, etc., which can be used to indicate specific error conditions.

Example

This C function raises a TypeError when the input is not a string -

#include <Python.h>

static PyObject* expect_string(PyObject* self, PyObject* args) {
const char* text;
if (!PyArg_ParseTuple(args, "s", &text)) {
PyErr_SetString(PyExc_TypeError, "Expected a string");
return NULL;
}
return PyUnicode_FromString(text);
}

static PyMethodDef MyMethods[] = {
{"expect_string", expect_string, METH_VARARGS, "Expects a string argument"},
{NULL, NULL, 0, NULL}
};

static struct PyModuleDef mymodule = {
PyModuleDef_HEAD_INIT,
"myextension",
NULL,
-1,
MyMethods
};

PyMODINIT_FUNC PyInit_myextension(void) {
return PyModule_Create(&mymodule);
}

How to Compile

Save the above C code in a file named myextension.c. Then create a setup.py file to build it -

from setuptools import setup, Extension

module = Extension("myextension", sources=["myextension.c"])

setup(
name="myextension",
version="1.0",
description="A simple C extension that expects a string",
ext_modules=[module]
)

In the terminal, run the following command to build the extension -

python3 setup.py build

You can then install the module or copy the generated .so (or .pyd on Windows) file into your Python script's directory.

Once compiled and installed, you can use it in your Python code -

import myextension

print(myextension.expect_string("hello")) # Output: hello

try:
print(myextension.expect_string(123)) # Raises: TypeError: Expected a string
except TypeError as e:
print("Caught exception:", e)

This allows Python code to catch the exception using try...except blocks, just like any other Python function.

Handling Exceptions in Python Code

After raising exceptions from C, you can catch them using standard Python error handling i.e., try...except blocks. This makes the user experience smooth even when the logic is implemented in C.

Example

The following Python code calls a C function that raises a ValueError when we pass a negative number. The exception is caught using normal Python syntax -

try:
myextension.check_positive(-3)
except ValueError as e:
print("Caught from C extension:", e)

We get the following output -

Caught from C extension: Negative value not allowed

Using Custom Exceptions in C Extensions

In addition to built-in exceptions, you can define and raise your own custom exception classes directly from your C extension.

Example

First, define a custom exception object during module initialization. This example creates a custom exception named MyCustomError -

static PyObject* MyCustomError;

PyMODINIT_FUNC PyInit_myextension(void) {
PyObject* m = PyModule_Create(&mymodule);
if (m == NULL)
return NULL;

MyCustomError = PyErr_NewException("myextension.MyCustomError", NULL, NULL);
Py_XINCREF(MyCustomError);
PyModule_AddObject(m, "MyCustomError", MyCustomError);
return m;
}

Once the exception is registered, you can raise it in any function like this -

if (some_error_condition) {
PyErr_SetString(MyCustomError, "Something went wrong!");
return NULL;
}

Catching Custom Exceptions in Python

From Python, you can catch the custom exception using try...except blocks as shown below -

try:
myextension.some_function()
except myextension.MyCustomError as e:
print("Caught custom error:", e)
Updated on: 2025-05-15T14:00:10+05:30

191 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements