Rag And Bone

Usually, when writing bindings for a C library in a high level language, there’s a sweet spot where you have to leave the relative safety of an API similar to the library you are wrapping and the idiomatic correctness dictated by the language itself.

For instance, in the PyClutter bindings a typical clutter.Behaviour constructor is lifted directly from the equivalend function call:

  behaviour = clutter.BehaviourDepth(alpha, -100, 100)

  behaviour = clutter_behaviour_depth_new (alpha, -100, 100);

The ClutterAlpha parameter, though, can ben NULL, so a “keep the API similar to the C equivalent” approach would be:

  behaviour = clutter.BehaviourDepth(None, -100, 100)

  behaviour = clutter_behaviour_depth_new (NULL, -100, 100);

This is, though, different from the “pythonic” approach; as Python has the syntactic sugar to support arguments with default values, the place for those parameters would be at the end of the function call:

  behaviour = clutter.BehaviourDepth(-100, 100)

Correctness issue aside, let’s add the fact that a pythonic approach allows the poor maintainer (yours truly) to drop a lot of hand-written code.

Unfortunately, though, the pythonic approach breaks the API1; more unfortunately, it breaks the API without giving a useful error message: the PyGObject bindings will raise a not very useful

  "TypeError: could not convert parameter 'depth_start' of type 'gint'"

exception when encountering the wrong value type for the argument2.

The question then is: should the idiomatic correctness sway in favour of the language or the underlying library?

Here the blog post ends. I’d like to have an answer for the question, but I’m still undecided; if anyone has some insight, especially Python developers, I’d appreciate a comment

  1. This is not a huge issue as it might seem: the underlying C library has already done that for us []
  2. The error message should be a more useful “could not convert value of type X, and parameter Y requires a value of type Z”, which would require a bit of tinkering in the bindings themselves and it wouldn’t be useful anyway because only the new versions would pick that change up []
This entry was posted in Hacking, Python. Bookmark the permalink.

6 Responses to Rag And Bone

  1. Tiago says:

    Why can’t you do it in the Pythonic way?

    def foo(*args):
    alpha = default_value
    try:
    alpha, spam, eggs = args
    except ValueError:
    try:
    spam, eggs = args
    except ValueError:
    raise TypeError(“foo() takes 2 or 3 arguments (%d given)” % len(args))
    native_foo(alpha, spam, eggs)

    The same could be done in PyC, couldn’t it?

  2. Mike Gratton says:

    Consistency is king! Well, it improves writeability, readability and hence maintainability, anyway. So do you err on the side of language consistence or API consistency?

    The former is useful for people already familiar with the language starting to use the API for the first time, the latter is useful for people starting to use the language for the first time. I would wager people encounter the former a lot more than the latter – we tend to stick with one or two languages but use a large number of different libraries.

    So I would say choose idiomatic correctness, where it makes sense. Having using wxPython, I shudder at the though of using yet another C API expressed as same in Python.

    In this particular situation, could you provided named parameters as a nicer(er) solution, i.e. something like: def BehaviourDepth(ClutterAlpha=None, x=None, y=None): ...?

    This means people can then do clutter.BehaviourDepth(x=-100, y=100), which is perfectly cromulent Python code.

  3. Pingback: Hacking » Blog Archive » Rag And Bone

  4. Speaking purely for myself, I much, much prefer Pythonic Python bindings to APIs, rather than slavishly implementing the C API. Lots of people won’t ever look at the C API and will just use the Python one. Of course, this does mean that the Python API (a) needs hand-tweaking and (b) needs separate documentation, both of which are bad if you’re the maintainer…

  5. ebassi says:

    @stuart

    good points. the pygobject bindings tend to err on the python side of things, so in this very case I would be working against them; as for the documentation, you’re very right. I’m puzzled about the lack of documentation tools at least for generating the API reference from the code; in perl we have a module extracting documentation markers and methods and generating perldoc pages, for instance, so I get bare-bones API reference by just binding functions.

  6. ebassi says:

    @mike and tiago:

    yes, I forgot to mention that named parameter passing is already supported, so python developers using it already are perfectly safe.

Comments are closed.