GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing docs.python.org — Your first C API extension module Your first C API extension module¶This tutorial will take you through creating a simple Python extension module written in C or C++. We will use the low-level Python C API directly. For easier ways to create extension modules, see the recommended third party tools. The tutorial assumes basic knowledge about Python: you should be able to define functions in Python code before starting to write them in C. See The Python Tutorial for an introduction to Python itself. The tutorial should be approachable for anyone who can write a basic C library.
While we will mention several concepts that a C beginner would not be expected
to know, like We will focus on giving you a “feel” of what Python’s C API is like. It will not teach you important concepts, like error handling and reference counting, which are covered in later chapters. We will assume that you use a Unix-like system (including macOS and Linux), or Windows. On other systems, you might need to adjust some details – for example, a system command name. You need to have a suitable C compiler and Python development headers installed.
On Linux, headers are often in a package like You need to be able to install Python packages.
This tutorial uses pip ( Note This tutorial uses APIs that were added in CPython 3.15. To create an extension that’s compatible with earlier versions of CPython, please follow an earlier version of this documentation. This tutorial uses C syntax added in C11 and C++20. If your extension needs to be compatible with earlier standards, please follow tutorials in documentation for Python 3.14 or below. What we’ll do¶Let’s create an extension module called #include <stdlib.h> int system(const char *command); Note that like many functions in the C standard library,
this function is already exposed in Python.
In production, use We want this function to be callable from Python as follows: >>> import spam >>> status = spam.system("whoami") User Name >>> status 0 Note The system command Start with the headers¶Begin by creating a directory for this tutorial, and switching to it
on the command line.
Then, create a file named In this file, we’ll include two headers: Add the following lines to #include <Python.h> #include <stdlib.h> // for system() Be sure to put Running your build tool¶With only the includes in place, your extension won’t do anything. Still, it’s a good time to compile it and try to import it. This will ensure that your build tool works, so that you can make and test incremental changes as you follow the rest of the text. CPython itself does not come with a tool to build extension modules; it is recommended to use a third-party project for this. In this tutorial, we’ll use meson-python. (If you want to use another one, see Appendix: Other build tools.)
First, add [build-system] build-backend = 'mesonpy' requires = ['meson-python'] [project] # Placeholder project information # (change this before distributing the module) name = 'sampleproject' version = '0' Then, create project('sampleproject', 'c') py = import('python').find_installation(pure: false) py.extension_module( 'spam', # name of the importable Python module 'spammodule.c', # the C source file install: true, ) Note See the meson-python documentation for details on configuration. Now, build install the project in the current directory ( python -m pip -v install . The Tip If you don’t have Note that you will need to run this command again every time you change your extension. Unlike Python, C has an explicit compilation step. When your extension is compiled and installed, start Python and try to import it. This should fail with the following exception: >>> import spam Traceback (most recent call last): ... ImportError: dynamic module does not define module export function (PyModExport_spam or PyInit_spam) Module export hook¶The exception you got when you tried to import the module told you that Python is looking for a “module export function”, also known as a module export hook. Let’s define one. First, add a prototype below the PyMODEXPORT_FUNC PyModExport_spam(void); Tip The prototype is not strictly necessary, but some modern compilers emit warnings without it. It’s generally better to add the prototype than to disable the warning. The After the prototype, add the function itself.
For now, make it return PyMODEXPORT_FUNC PyModExport_spam(void) { return NULL; } Compile and load the module again. You should get a different error this time. >>> import spam Traceback (most recent call last): ... SystemError: module export hook for module 'spam' failed without setting an exception Simply returning The slot table¶Rather than The information should be defined in an array of
PyABIInfo_VAR(abi_info); static PySlot spam_slots[] = { PySlot_STATIC_DATA(Py_mod_abi, &abi_info), PySlot_STATIC_DATA(Py_mod_name, "spam"), PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"), PySlot_END }; The The For both Note The array is defined as Return this array from your export hook instead of PyMODEXPORT_FUNC PyModExport_spam(void) { return spam_slots; } Now, recompile and try it out: >>> import spam >>> print(spam) <module 'spam' from '/home/encukou/dev/cpython/spam.so'> You have an extension module!
Try The next step will be adding a function. Exposing a function¶To expose the One of the simplest ways to write glue code is a “ Add such a function above the slots array: static PyObject * spam_system(PyObject *self, PyObject *arg) { Py_RETURN_NONE; } For now, we ignore the arguments, and use the Recompile your extension to make sure you don’t have syntax errors.
We haven’t yet added Method definitions¶To expose the C function to Python, you will need to provide several pieces of
information in a structure called
Because modules typically create several functions, these definitions
need to be collected in an array, with a zero-filled sentinel at the end.
Add this array just below the static PyMethodDef spam_methods[] = { { .ml_name="system", .ml_meth=spam_system, .ml_flags=METH_O, .ml_doc="Execute a shell command.", }, {NULL, NULL, 0, NULL} /* Sentinel */ }; As with module slots, a zero-filled sentinel marks the end of the array. Next, we’ll add the method to the module.
Add a PyABIInfo_VAR(abi_info); static PySlot spam_slots[] = { PySlot_STATIC_DATA(Py_mod_abi, &abi_info), PySlot_STATIC_DATA(Py_mod_name, "spam"), PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"), PySlot_STATIC_DATA(Py_mod_methods, spam_methods), PySlot_END }; Recompile your extension again, and test it.
Be sure to restart the Python interpreter, so that You should now be able to call the function: >>> import spam >>> print(spam.system) <built-in function system> >>> print(spam.system('whoami')) None Note that our Check that the function accepts exactly one argument, as specified by
the >>> print(spam.system('too', 'many', 'arguments')) Traceback (most recent call last): ... TypeError: spam.system() takes exactly one argument (3 given) Returning an integer¶Now, let’s take a look at the return value.
Instead of The Python C API provides a function to create a Python To call it, replace the static PyObject * spam_system(PyObject *self, PyObject *arg) { int status = 3; PyObject *result = PyLong_FromLong(status); return result; } Recompile, restart the Python interpreter again, and check that the function now returns 3: >>> import spam >>> spam.system('whoami') 3 Accepting a string¶Finally, let’s handle the function argument. Our C function, The other one, There’s a slight type mismatch here: Python’s The function to encode a Python string into a UTF-8 buffer is named
static PyObject * spam_system(PyObject *self, PyObject *arg) { const char *command = PyUnicode_AsUTF8AndSize(arg, NULL); int status = 3; PyObject *result = PyLong_FromLong(status); return result; } If
If For the static PyObject * spam_system(PyObject *self, PyObject *arg) { const char *command = PyUnicode_AsUTF8AndSize(arg, NULL); if (command == NULL) { return NULL; } int status = 3; PyObject *result = PyLong_FromLong(status); return result; } To test that error handling works, compile again, restart Python so that
>>> import spam >>> spam.system(3) Traceback (most recent call last): ... TypeError: bad argument type for built-in operation Now, all that is left is calling the C library function static PyObject * spam_system(PyObject *self, PyObject *arg) { const char *command = PyUnicode_AsUTF8AndSize(arg, NULL); if (command == NULL) { return NULL; } int status = system(command); PyObject *result = PyLong_FromLong(status); return result; } Compile your module, restart Python, and test.
This time, you should see your username – the output of the >>> import spam >>> result = spam.system('whoami') User Name >>> result 0 You can also test with other commands, like >>> import spam >>> result = spam.system('nonexistent-command') sh: line 1: nonexistent-command: command not found >>> result 32512 The result¶Congratulations! You have written a complete Python C API extension module, and completed this tutorial! Here is the entire source file, for your convenience: /// Includes #include <Python.h> #include <stdlib.h> // for system() /// Implementation of spam.system static PyObject * spam_system(PyObject *self, PyObject *arg) { const char *command = PyUnicode_AsUTF8AndSize(arg, NULL); if (command == NULL) { return NULL; } int status = system(command); PyObject *result = PyLong_FromLong(status); return result; } /// Module method table static PyMethodDef spam_methods[] = { { .ml_name="system", .ml_meth=spam_system, .ml_flags=METH_O, .ml_doc="Execute a shell command.", }, {NULL, NULL, 0, NULL} /* Sentinel */ }; /// Module slot table PyABIInfo_VAR(abi_info); static PySlot spam_slots[] = { PySlot_STATIC_DATA(Py_mod_abi, &abi_info), PySlot_STATIC_DATA(Py_mod_name, "spam"), PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"), PySlot_STATIC_DATA(Py_mod_methods, spam_methods), PySlot_END }; /// Export hook prototype PyMODEXPORT_FUNC PyModExport_spam(void); /// Module export hook PyMODEXPORT_FUNC PyModExport_spam(void) { return spam_slots; } Appendix: Other build tools¶You should be able to follow this tutorial – except the
Running your build tool section itself – with a build tool other
than The Python Packaging User Guide has a list of recommended tools; be sure to choose one for the C language. Workaround for missing PyInit function¶If your build tool output complains about missing // A workaround void *PyInit_spam(void) { return NULL; } This is a shim for an old-style initialization function, which was required in extension modules for CPython 3.14 and below. Current CPython does not need it, but some build tools may still assume that all extension modules need to define it. If you use this workaround, you will get the exception
Compiling directly¶Using a third-party build tool is heavily recommended, as it will take care of various details of your platform and Python installation, of naming the resulting extension, and, later, of distributing your work. If you are building an extension for as specific system, or for yourself only, you might instead want to run your compiler directly. The way to do this is system-specific; be prepared for issues you will need to solve yourself. Linux¶On Linux, the Python development package may include a gcc --shared $(python3-config --cflags --ldflags) spammodule.c -o spam.so This should generate a Footnotes
© Copyright 2001 Python Software Foundation.
This page is licensed under the Python Software Foundation License Version 2. Examples, recipes, and other code in the documentation are additionally licensed under the Zero Clause BSD License. See History and License for more information. The Python Software Foundation is a non-profit corporation. Please donate. Last updated on Nov 12, 2026 (11:13 UTC). Found a bug? Created using Sphinx 9.1.1. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |