Sunday, 18 May 2008

Calling Vala code from Python

Today I gave a try at using vala and python together in my OpenMoko mobile phone.
Python is a powerful object oriented script language, and vala is a C gobject compiler that makes it easy to generate object oriented code in C. It is quite easy to mix python and vala in a same programme, allowing us to write very quickly modular and fast applications.

So here is how it works :

Let say I have a vala module, that defines one class, with one public method. Here is the file (test.vala) :

using GLib;

namespace Test {
public class Test : Object {
public int sum(int x, int y) {
return x + y;
}
}
}

I can generate the C/gobject code for this module, using vala :

valac -C test.vala

this command will create test.c and test.h. These files are conform C and can be compiled with gcc (in fact these are the files people using gobject in C would write. If we have a look at it, we understand the pain that it is to write gobject code in C !).
But to be able to call the functions from python, we need to create wrapper to the python C interface. This can be easily done using some tools (included in the debian pygtk-dev package). For more information, see this page.

Here are the commands that generate our C python interface :

python /usr/share/pygtk/2.0/codegen/h2def.py test.h > test.defs
pygtk-codegen-2.0 -o test.override -p test test.defs > test_wrap.c

Where 'test.override' is a compulsory file that looks like this :

%%
headers
#include
#include "pygobject.h"
#include "test.h"
%%
modulename test
%%
import gobject.GObject as PyGObject_Type
%%
ignore-glob
*_get_type
%%

Alright we now have a file called test_wrap.c, that contains all the wrapper functions from the python C interface to gobject C.

The next step is to actually create the C library callable from python. this library must contains a function 'inittest' (this is the function that will be called when we import the module from python.) The file looks like this (test_module.c) :

#include

void test_register_classes (PyObject *d);
extern PyMethodDef test_functions[];

DL_EXPORT(void)
inittest(void)
{
PyObject *m, *d;
init_pygobject();
m = Py_InitModule("test", test_functions);
d = PyModule_GetDict(m);
test_register_classes(d);
if (PyErr_Occurred ()) {
Py_FatalError ("can't initialise module test");
}
}

And then we are almost done... finally we compile and link everything :

CFLAGS="`pkg-config --cflags gtk+-2.0 pygtk-2.0` -I/usr/include/python2.5/ -I."
LDFLAGS=`pkg-config --libs gtk+-2.0 pygtk-2.0`
gcc -c -o test.o test.c
gcc $CFLAGS -c test_wrap.c -o test_wrap.o
gcc $CFLAGS -c test_module.c -o test_module.o
gcc $LDFLAGS -shared test.o test_wrap.o test_module.o -o test.so

And now we finally have our library : test.so, that can be directly used from python. Here is our python script (test.py):

import test
t = test.Test()
print t.sum(2,3)

Conclusion :
This combination (python + vala) is ideal to write applications for OpenMoko : there is no need to compile the python part, and since the vala code generate C code, it can be easily compiled for different targets.
So no excuses not to write great apps for OpenMoko :)

4 comments:

Anonymous said...

While that might be cool and useful for some, I prefer Cython (and hopefully soon, RPython).

I don't see the point of learning yet another barely used C-style language.

Guillaume Chéreau said...

I never heard of Cython before ! It looks very promising indeed ! I know pyrex, but I had been quite disappointed with it.

One of the advantage of vala is that you can use the gobject libraries. Also when you write a library in vala you can then use it from several languages, not just python.

That being said, I agree that Cython may be a better choice in general cases. I will definitively spend some time learning it.

Anonymous said...

Nice work! In the - hopefully not so distant - future, this will get even easier. PyBank will be able to generate Python bindings to GObject libraries on the fly, i.e. you won't need to write .override files or run pygtk-codegen, it will just work.

The information required for the bindings will be read from a GObject Introspection Metadata file, which the Vala compiler can automatically generate.

Guillaume Chéreau said...

Wow PyBank is very interesting as well.

As far as I understand it will be somehow similar to the ctypes module, but with the ability to access gobject as well as plain C functions.

I like ctypes a lot, in fact this is what I use most of the time when I want to write a small part of my code in C.

Yeah, I will keep an eye on PyBank.