Skip to content

Latest commit

 

History

History
126 lines (94 loc) · 3.11 KB

File metadata and controls

126 lines (94 loc) · 3.11 KB

Exercise 06: Minimal "Hello, World!" C-extension

Note

There is a very good and detailed guide in the official Python documentation that you can check for more information. Check it here.

In this exercise, we will create a new Python module written in C. This might sound very complicated, but don't be scared!

While writing the code, there are a few steps we need to follow in order to get to a state where we can write the C-code.

Let's open a hello.c file and start our adventure in five steps!

1. Add the base C function

The function needs to follow a few details from CPython in order to be compatible. To return a string, we need to convert a C-string to a Python string.

Here's the code for the first step:

#include <Python.h>

static PyObject* hello(PyObject* self, PyObject* args) {
    char *msg = "Hey there!";
    return Py_BuildValue("s", msg);
}

We still have a few steps before we can compile it!

2. Add the methods definition

Because our module will have only one function, we will add only that one, and then add a couple of C-None called NULL to identify when the definition finishes.

Here's the methods definitions:

static PyMethodDef the_methods[] = {
    {"hello", (PyCFunction)hello, METH_NOARGS, NULL},
    {NULL, NULL, 0, NULL}
};

Notice that we have a few details that we will not explore too much like casting the function as a PyCFunction or setting that the function doesn't have any arguments with METH_NOARGS.

3. Add the module definition

Before initializing the module, we need to define the structure with a few placeholder elements, but the most important part is to pass the module name and also the methods definitions:

static struct PyModuleDef module = {
    .m_base = PyModuleDef_HEAD_INIT,
    .m_name = "mymodule",
    .m_size = 0,
    .m_methods = the_methods
};

4. Initialize the module

For the last step from the C-side of things, we need to initialize the module in order to create the module.

PyMODINIT_FUNC PyInit_mymodule(void){
    return PyModuleDef_Init(&module);
}

5. Creating a package (wheel)

Create a pyproject.toml alongside the hello.c file, with the following content:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "mymodule"
version = "1.0"

[tool.setuptools]
ext-modules = [
  {name = "mymodule", sources = ["hello.c"]}
]

Install the build and setuptools Python packages, and then run:

python -m venv env
source env/bin/activate  # for macOS and Linux
env\Scripts\activate     # for Windows
pip install build setuptools

python -m build -w

Important

Make sure the python and pip executable is the one that has setuptools and build installed.

If everything succeeds, you will have a .whl package in the dist/ directory that you can install, to later execute like:

>>> from mymodule import hello
>>> hello()
"Hey there!"

[!OPTIONAL] What if you would like to do this in C++? Check out std::string as a type and replace the content of the hello function