[capi-sig] Announcing project PyBindGen

Stefan Behnel python_capi at behnel.de
Tue Sep 25 15:12:47 CEST 2007

Gustavo Carneiro wrote:
> Come on, I wouldn't say this interface is unreadable.  It's stuff like this:
>     mod = Module('foo')
>     Foo = CppClass('Foo', automatic_type_narrowing=True)
>     mod.add_class(Foo)
>     Foo.add_static_attribute(ReturnValue.new('int'), 'instance_count')
>     Foo.add_constructor(
>         CppConstructor([Parameter.new('std::string', 'datum')]))
>     Foo.add_constructor(CppConstructor([]))
>     Foo.add_method(CppMethod(ReturnValue.new('std::string'),
> 'get_datum', []))
> Not that hard to write this, is it?

I didn't say it's unreadable and I didn't say it was hard to write. I'm just
saying that you shouldn't have to write that at all if all you get is a
straight mapping between a C(++) API and a Python API. That's what wrapper
generators are there for: parse the interface description and generate the
wrapper with as little human interaction as possible. And, that's what the
wrapper languages (that you don't want to require people to learn) are good at.

> For instance, I don't find this very readable:
> static PyObject *__pyx_f_4spam_4Spam_get_amount(PyObject *__pyx_v_self,
> PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/

So far for the signature.

> static PyObject *__pyx_f_4spam_4Spam_get_amount(PyObject *__pyx_v_self,
> PyObject *__pyx_args, PyObject *__pyx_kwds) {
>   PyObject *__pyx_r;
>   PyObject *__pyx_1 = 0;
>   static char *__pyx_argnames[] = {0};
>   if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "",
> __pyx_argnames))) return 0;
>   Py_INCREF(__pyx_v_self);

That's for argument parsing - no need to look at that.

>   /* "/Users/robert/sage/pyrex/cython-
> <>":16
>  *
>  *   def get_amount(self):
>  *     return self.amount             # <<<<<<<<<<<<<<
>  *
>  *   def set_amount(self, new_amount):
>  */

Here we go, this is the code that was translated into the C code below.

>   __pyx_1 = PyInt_FromLong(((struct __pyx_obj_4spam_Spam
> *)__pyx_v_self)->amount); if (unlikely(!__pyx_1)) {__pyx_filename =
> __pyx_f[0]; __pyx_lineno = 16; goto __pyx_L1;}
>   __pyx_r = __pyx_1;
>   __pyx_1 = 0;
>   goto __pyx_L0;

The first line is so long to keep you from looking at it, so if you ignore the
error handling, what you see is:

__pyx_1 = PyInt_FromLong(((struct __pyx_obj_4spam_Spam *)__pyx_v_self)->amount);

__pyx_r = __pyx_1;

Relatively straight assignment code, split into code for getting the value and
code for setting the value.

>   __pyx_r = Py_None; Py_INCREF(Py_None);
>   goto __pyx_L0;
>   __pyx_L1:;
>   Py_XDECREF(__pyx_1);
>   __Pyx_AddTraceback("spam.Spam.get_amount ");
>   __pyx_r = 0;
>   __pyx_L0:;
>   Py_DECREF(__pyx_v_self);
>   return __pyx_r;
> }

Again, ignore the clean up code.

Admittedly, it's far from hand-optimised, aesthetic code, but it's definitely

> Compared with this code generated by pybindgen:
> static PyObject *
> _wrap_fooinvoke_some_object_get_prefix()
> {
>     PyObject *py_retval;
>     std::string retval;
>     retval = invoke_some_object_get_prefix();
>     py_retval = Py_BuildValue("s#", retval.c_str(), retval.size());
>     return py_retval;
> }

Ok, but that's almost exactly the code you wrote by hand, step by step through
your Python API. So the question is: what's the advantage compared to writing
the code *directly* by hand, i.e. in plain C?

Oh, and: you're not passing any arguments here. If you were, your function
would look a bit more bloated, just like the Pyrex code.

> Pybindgen generated code is almost as clean as the code you would write
> by hand.  To me that counts a lot.  I know some people don't care what
> happens underneath the tools they use as long as it works.  I am not
> that kind of person.

I do care a lot, that's why I keep submitting patches to Cython. The better
the code gets that Cython generates, the less I have to care about the
generated C code when I write Cython code.

I think *that* is the essence of a code generator tool.


More information about the capi-sig mailing list