I asked a friend to create a function and put it in a module for me. I must not have stated that I wanted the function to be written in Python because what I got back was in C. Not exactly what I needed. After some thought and discussion, I said “Thank you.”
I know of SWIG, SIP, Boost.Python, Pyrex, and ctypes. These are all methods of adding C code to a Python script. SWIG and SIP will work for medium to large libraries. They are not part of the “batteries included”. Good solutions if you have small to medium size libraries to convert. Pyrex is interesting but very new to me and again not part of “batteries included.” Boost.Python requires you to be an expert in C++ and they have a reputation of not maintaining backward compatibility. I cannot afford to be burned on this a second time. ctypes is part of the “batteries included.” It works well with a small number of C functions.
This blog was written as a reminder to me of how to set this up. I have written, as always, the simplest program I can for starting out in this area.
Here is the C function:
It takes in 1 variable and returns 1 variable.
Passing in more than one parameter is just listing the other parameters in C. There is no need to worry about the order of parameters. None, integers, longs, byte, and Unicode strings are the only native parameters that can be passed directly to a function. None is passed in as a null pointer, strings are passed as a pointer to a data block. Other data types and structures can be used. They require the building of unique data objects for each object type. You can pass back pointers to data that are converted to the correct type in Python.
I created a second function called goodbye.c to show a little more about why I created the library using the following procedure.
Step 1: Creating Position Independent Code (PIC) object
gcc -fPIC -o hello.o hello.c
gcc -fPIC -o goodbye.o goodbye.c
The -fPIC creates the position independent code. You may see this as -fpic. -fPIC is truly independent while -fpic for some environments creates slightly smaller code. The -o <name>.o names the output object modules. They must end in .o. The code has been compiled into binary objects.
The <name>.c is the name of the source code file.
I did not have to build or specify any unique data object because my functions are passing and returning only data types that the ctype module understands.
If you are planning on a lot of changes to your library, you need to set up a repository for these object modules so you do not have to recreate them each time you want to update the library. There is no method of adding or subtracting data from a shared object library. You just create a new one.
Step 2: Creating the Shared Object Libraries
gcc -shared -wl,-soname,libhg.so.1 -o libhg.so.1.0 hello.o goodbye.o
The -shared is creates the shared library. The -wl,-soname,libhg.so.1 passes the shared object name to the linker. The lib is for library hg and is replaced by the name of your library. The .so is for shared object. The .1.0 represents this is the first release. The command syntax does require it to be created. The -o libhg.so.1.0 is the name of the library file.
You can see the objects in the library with the command:
nm -D --defined-only libhg.so.1.0
What is of interest to us are the entries for hello and goodbye.
Step 3: Creating Python Module Wrapping libhg.so.1.0
The magic is in line 3. This creates the object templib and each of the functions in the C library become an attribute of the object. By using the absolute path in the cytpes.CDLL call, I do not have to have an exported LD_PATH_LIBRARY variable to find the shared object library. Line 5 tells ctypes to convert the pointer to a character string to a string variable in Python. Please note that the use of CDLL does NOT mean that this is making a Windows-style dll library. This library is usable in Python on any Python supported O/S. Line 6 was added just to make this look more like a standard module, to make calling from a Python program just libhg.hello("john"). Lines 8 and 9 repeat the actions of lines 5 and 6 for the function goodbye.
Step 4: Testing
Execute the following: