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 = {
    PyModuleDef_HEAD_INIT,
    "clib",
    "",
    -1,
    ClibMethods
};

PyMODINIT_FUNC
PyInit_clib(void)
{
    return PyModule_Create(&ClibModule);
}

setup script

the setup script setup.py is also very simple:

from setuptools import Extension
from setuptools import setup

setup(
    name='foo',
    py_modules=['foo.pylib'],
    ext_modules=[Extension('foo.clib',['foo/clib.c'])],
)

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);

test

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))"
7
# python3 -c "import foo.clib; print(foo.clib.cadd(3, 4))"
7

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))"
7
# python3 -c "import foo; print(foo.cadd(3, 4))"
7