
Data Structure
Networking
RDBMS
Operating System
Java
MS Excel
iOS
HTML
CSS
Android
Python
C Programming
C++
C#
MongoDB
MySQL
Javascript
PHP
- Selected Reading
- UPSC IAS Exams Notes
- Developer's Best Practices
- Questions and Answers
- Effective Resume Writing
- HR Interview Questions
- Computer Glossary
- Who is Who
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)