Language Bindings for the C++ API: Python partially working

Blog post by jalopeura on Fri, 2011-06-24 01:21

I've just uploaded some files onto http://dev.osdrawer.net/projects/perl-haiku-kits/files They are minimal implementations (window with a button) for Perl and Python. The Perl one works fine, but I'm still having issues with the Python.

Here are some of the issues with Python:

Apparently by convention packages start with a lower-case letter. The bindings are currently in the 'Haiku' package. This would be a trivial change; it depends on how important it is to Haiku's Python user community.

PyModules and PyTypes (= classes) cannot share the same name, as far as I can tell. Thus, in order to have constants and plain functions available from (for example) Haiku.Application, the Application PyType must be in a separate namespace, currently Haiku.Application.Application. Depending on what the community wants, I could move all the constants and plain functions into the ApplicationKit PyModule, and have the PyType be Haiku.Application.

The really big problem, however, is passing objects. BWindow::MessageReceived gets a BMessage object. In order to pass this to Python, I need to have a PyType. But the PyType in question is defined in the ApplicationKit, which is a separate package. So I don't have access to it.

In Perl, this was not a problem; I simply used a string containing the Perl class name, and as long as the user had loaded the relevant package, Perl took care of it. But Python wants the PyType, not just a string. I'm still looking into a way to get around this problem. I'm trying to do it by eval'ing some Python code, but so far I have been unsuccessful. If I can't do it any other way, I could export the PyType from the other package, but then Python would be loading the .so and I would be loading it a second time, in addition to the extra overhead of exporting and importing. It just seems like a waste of resources.

In any case, the Python test script displays the window, and calls event hooks on the Application object (ArgvReceived, ReadyToRun, QuitRequested), but when you click the button and it tries to call the Window's MessageReceived event, it dies.

Comments

Re: Language Bindings for the C++ API: Python partially working

While there is a convention for package and module names to be lower case (to avoid file system trouble), this is often ignored. On the other hand, class names are almost always in CamelCase (except built-in types).

As for namespaces, you unfortunately seem to have misunderstood the Python concept, which is very different from the one used by Perl.

Unlike C++ oder Perl, Python packages and modules have implicit namespaces, and modules may (do) contain multiple classes. The Pythonic way would be to have a package "Haiku" containing one module for each kit. Each of these modules should contain all the kit's classes. Also, please don't put the classes in the package global namespace. They belong in their respective kit's namespace.

In the test program, it would look like this:

import Haiku.ApplicationKit
 
class MyApplication(Haiku.ApplicationKit.Application):

or:

from Haiku.ApplicationKit import Application
 
class MyApplication(Application):

Edit: The following paragraph required correction:

Also, as you noticed, dependencies across Python packages are a problem inside C/C++ extensions. Extensions should be self contained. This is not possible with the Haiku API, if you split it up.

Re: Language Bindings for the C++ API: Python partially working

P2501 wrote:

As for namespaces, you unfortunately seem to have misunderstood the Python concept, which is very different from the one used by Perl.

If by "misunderstood the concept" you mean "were not aware of the conventions", then yes, I agree. Otherwise, I don't know what you mean. I have become all too familiar with the "everything is an object" mentality of Python.

Quote:

Unlike C++ oder Perl, Python packages and modules have implicit namespaces, and modules may (do) contain multiple classes. The Pythonic way would be to have a package "Haiku" containing one module for each kit. Each of these modules should contain all the kit's classes. Also, please don't put the classes in the package global namespace. They belong in their respective kit's namespace.

Yes, I'm already putting multiple classes into a single module; for example, Window (the base object) and CustomWindow (the one you can subclass to respond to events)* live in the same module. The difficult part is that Window (the object) and Window (the namespace/module) cannot have the same name. This leads to rather long class names.

*Before you ask, the reason I have two versions of Window is to avoid overhead. Every time an object responds to an event, the extension has to determine the Python object, translate the arguments into Python objects, look up the Python method. If there's no subclass, it's just going to find the extension-defined base method, which will translate the arguments into C++ data types and call the base class version. So if the user isn't subclassing it, it makes no sense to bother with all that.

Quote:

In the test program, it would look like this:

import Haiku.ApplicationKit
 
class MyApplication(Haiku.ApplicationKit.Application):

or:

from Haiku.ApplicationKit import Application
 
class MyApplication(Application):

Thank you. Now I have an organizing principle to follow, although the long class names (Package.Module.Class, rather than simply Package.Class) really stick out, in my opinion. Of course, they were already that long the way I'm currently handling it; it just seems odd to me (as an outsider) that Python forces this on users. But I'm willing to follow Python conventions and give the user what they want.

However, I still have the problem of constants. For example, there are constants, enums, and in some cases plain functions (i.e., not object or class methods) defined in the header files for the various objects. They logically belong with that object. But Python won't let me place, for example, B_NO_SPECIFIER into the same namespace with the Message object, because that namespace holds a class.

So which follows Python convention better: Putting them into the package (Haiku), putting them into the kit module (Haiku.ApplicationKit), or putting them into a separate module (either Haiku.MessageConstants or Haiku.ApplicationKit.MessageConstants)?

Quote:

Also, as you noticed, dependencies across Python packages are a problem inside C/C++ extensions. Extensions should be self contained. This is not possible with the Haiku API, if you split it up.

I ws trying to avoid putting all the kits into the same extensions. For example, why should a user be required to load the Storage kit is he's not going to use it? But the Storage kit uses elements from the Application and Interface kits, so it needs access to them.

And yes, I know a Python user can from-import and only get the desired modules/classes, but Python still has to load the entire .so into memory.

Re: Language Bindings for the C++ API: Python partially working

Okay, we seem to have a problem with the definition of "name". ;-) Let's look at this snippet:

class MyWindow(Haiku.Window.Window):

Here, "Haiku.Window.Window" is not a class name. The class name is "Window". It lives in the namespace of the module "Window" who exists in the package global namespace of the package "Haiku". So, "Haiku.Window.Window" is more like a path, if you will, a bit like nested namespaces in C++ or class paths in Java. In Python, of course, it's actually an object reference hierarchy.

Long story short, if the path is Haiku.Window.Window, the class and module do have the same name. Because C has no real concept of namespaces (and in C++, they work quite differently), the names within C/C++ extensions have to be the full