a python project may contain both pure python and c extension modules; now we see how to package them together;

specifically, assume we are writing a package foo, which provides an adder function; in fact, two: one implemented in pure python, the other in c as an extension; we want to make both functions available to our package users; for example, users can call foo.pyadd() or foo.cadd(), at they will;

to make things clean, we put each adder function in its own module; our project dir looks something like this:

├── foo
│   ├── clib.c
│   ├── __init__.py
│   └── pylib.py
└── setup.py

function implementations

the function implementations are trivial:

##  pylib.py
def pyadd(a, b):
    return a + b
//  clib.c
#include <Python.h>

static int add(int a, int b)
    return a + b;

static PyObject *
clib_cadd(PyObject *self, PyObject *args)
    int a, b;
    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;
    int c = add(a, b);
    return PyLong_FromLong(c);

static PyMethodDef ClibMethods[] = {
    { "cadd", clib_cadd, METH_VARARGS, ""   },
    { NULL,   NULL,      0,            NULL }

static struct PyModuleDef ClibModule = {

    return PyModule_Create(&ClibModule);

setup script

the setup script setup.py is also very simple:

from setuptools import Extension
from setuptools import setup


here we have two modules:

  • pure python module foo.pylib;

  • c extension module foo.clib;

it is straightforward to list them in py_modules and ext_modules, respectively; feel free to use packages instead of py_modules if you want to list a whole package instead of each of its modules; but packages only includes pure python modules (not c extension modules);


to test our project, build and install:

python3 build setup.py
python3 install setup.py

then test both adder functions:

# python3 -c "import foo.pylib; print(foo.pylib.pyadd(3, 4))"
# python3 -c "import foo.clib; print(foo.clib.cadd(3, 4))"

to make things convenient, we can modify foo/__init__.py to import both adder functions:

from .pylib import pyadd
from .clib import cadd

rebuild and reinstall; then our test becomes even simpler:

# python3 -c "import foo; print(foo.pyadd(3, 4))"
# python3 -c "import foo; print(foo.cadd(3, 4))"