A python ctypes tutorial by example

For my work I needed to find a package that performed spin weighted spherical harmonic transformations and integrate this into a larger body of code.

The library for the spin weighted sphereical harmonic transformations was Huffenberger's and Wandelt's (arXiv) spinsfast. Their library is written in C. My code is written in Python. So naturally I needed to combine the too.

I'd heard it claimed that Python integrates easily with C, so I expected this to not be a big job. This was especially so since the version of Python I used was built with Cython. In any case after quite a bit of reading and searching, it seemed that I needed to build wrappers for the functions in Huffenberger's and Wandelt's using either ctypes or Cython it self. It was clear that using Cython would require more C knowledge than I currently had, especially regarding the Python side implementation, so I picked ctypes. ctypes is a Python module, so at least the code would be in a language that I'm more familiar with. It turns out that to understand what ctypes does you need a good grasp of C. It'd been a long time since I'd programmed in C, so being a bit rusty I jumped straight in.

The rest of this post is a discussion of the method I eventually ended up adopting for writing the wrappers as well as links to the relevant documentation.

Wrappers for C code, using ctypes consist, essentially, of a series of lines that declare the functions for the ctypes code. These are essentially like C header files. This lines of code need to specify the type of the return and the types of the inputs to the functions that'll be called in the C code. Apparently this tells the ctypes module how to convert the python objects to C style objects (or rather, I guess how to convert the Cython objects to "standard" ctypes objects).

The ctypes documentation about this is... patchy, particularly for sending arrays to C or collecting structs from C. It can all be done, it's just not clear. Now, if I'd had more recent C experience then perhaps it'd have been really easy.

So after compiling the spinsfast library to a shared object library. I could begin. Here is one the functions that I needed to implement:

void spinsfast_forward_transform(fftw_complex * restrict a, const int Ntransform, const int *spins, const int lmax, fftw_complex * restrict Jmm_set, int DeltaMethod, void *Deltawork)

Some digging in the code and reading of documentation was required to determine what all these things were. The type fftw_complex is a struct defined by libfftw if not using a c99 compliant compiler, otherwise it's just a normal complex type. The pointers are all references to arrays of the relevant type except for Deltawork which is a custom struct.

Rather than using the ctypes method for constructing and "array type" I used the ctypeslib for numpy. This provides some convenience functions that basically do the same thing.