Wrapping C libraries in Python


Introduction

Given that the library code is handy it’s a better to write a simple example program to test all the function that are supposed to be exposed by the python wrapper module. For this example I’m using devmem2 utility. The reason behind is that it is very small, with limited functions and requires elevated access as well.

Code

Full code can be found here

Selecting Lib and Functions to expose

As mentioned above the first step is to package and test the code using an example file. Once we have the C build up and running it’s time to expose the devmem2.h to python interface C file.

Functions about to get exposed

// read the value at given memory addr
unsigned long read_addr(off_t target);
// write the value at given memory addr
unsigned long write_addr(off_t target, unsigned long writeval);
// print usage
void usage(void);

Writing Python interface

The interface for python C file follow a standard structure as explained here

All the functions that are needed to exposed are defined using this template

static PyObject* foo(PyObject *self, PyObject *args) {
    // call C function here and collect result if any
    // return result
    // return Py_BuildValue("pattern", vals...)
    // return Nothing
    // Py_RETURN_NONE
}

Complete example for reading an address

static PyObject* devmem_read(PyObject *self, PyObject *args) {
    int ok;
    unsigned long addr;
    unsigned int value = 0;
    ok = PyArg_ParseTuple(args, "l", &addr);
    if (ok) {
        value = read_addr((off_t)addr);
    }
    return Py_BuildValue("i", value);
}

Exposing Interfaced functions

Functions and types defined here are exported as a extension module, as explained here

Wrapper python module is defined with a different name and it specifies this auto-generated module as its extension module in its setup.py


module = Extension('_devmem',
                   define_macros=define_macros,
                   extra_compile_args=extra_compile_args,
                   sources=c_sources,
                   include_dirs=['c_src'])

setup(
    ...
    ext_modules=[module],
    ...
)

In its code the wrapped module can call the internal module _devmem as any other python module. Here it is exposing a user friendly function to call read_mem function of devmem2 C library

import _devmem

def read_mem(addr=None):
    return _devmem.devmem_read(addr)

Using Python Package

Once built and installed the python package can be used to perform devmem2 functions.

import pydevmem

print(pydevmem.read_mem(0))
print(pydevmem.write_mem(0, 0))

Since this operation requires elevated access it is advisable to run with sudo.