PyMaemo/Accessing APIs without Python bindings

This is an on-going document, and will be linked on the PyMaemo main page once finished

Contents

Introduction

There are many libraries written in C that do not have native Python bindings yet. In Maemo, one of such libraries is libabook, used to manipulate the address book on Maemo devices.

While a full binding is still very useful, in most cases you need to use just a couple of functions and data structures to get your work done. Instead of waiting for a binding to be implemented, you can use Python's "ctypes" module, which allows to directly call functions and access data structures from C libraries.

This article will explain how to do call C library functions using ctypes, using libabook as an example. Most of the code snippets were based on source code from the "Hermes"[1] application.

This page is not meant to be a complete ctypes guide; for that, be sure to read the documentation at http://docs.python.org/library/ctypes.html.

Basic usage

Let's say you want to use libc's printf, for some reason. All you need in python is:

import ctypes
libc = ctypes.CDLL('libc.so.6')
libc.printf('Hello world!')

In a few words, you create an object correspondent to the library you need and use it to call the function directly. You can also store the functions in plain python objects to use them easily later:

c_printf = libc.printf
c_printf('Hello libc')

Remember that those are C functions, not Python ones, so you must supply arguments of the correct type to avoid undefined behavior. As a example, if I pass an integer to the above function, I get

>>> c_printf(1)
Segmentation fault (core dumped)

Wrapping function arguments

Quoting http://docs.python.org/library/ctypes.html#calling-functions:

None, integers, longs, byte strings and unicode strings are the only native Python objects that can directly be used as parameters in these function calls. None is passed as a C NULL pointer, byte strings and unicode strings are passed as pointer to the memory block that contains their data (char * or wchar_t *). Python integers and Python longs are passed as the platforms default C int type, their value is masked to fit into the C type.

For all other types, a ctypes C wrapper must be used; you can find them at http://docs.python.org/library/ctypes.html#fundamental-data-types.

An usage example of these wrappers is shown below:

>>> libc.printf("a integer: %d, a double: %f\n", 42, 3.14)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 3: <type 'exceptions.TypeError'>: Don't know how  to convert parameter 3

Integers are fine, but ctypes can't handle Python doubles directly. ctypes.c_double() will solve the problem here:

>>> libc.printf("a integer: %d, a double: %f\n", 42, ctypes.c_double(3.14))
a integer: 42, a double: 3.140000

References

  1. http://hermes.garage.maemo.org/