Automating shadow class generation: the make_python_shadow_classes.py script

Using the Eiffel source and generated header file for our information sources in order to automatically generate Python shadow classes would work just fine, but it has some problems: first, the information for all the exported classes is scattered throughout the classes' individual source code files, which increases the complexity of our task. Second, Eiffel source code is actually rather difficult to parse--much more so than, say, Lisp or even Python. So parsing the Eiffel source isn't our best bet. Parsing the generated headers isn't bad, but doesn't provide us with any sort of way to group functions of a given class together.

The answer lies in a fairly obvious place: the cecil.se file, which contains a list of all exported functions, and the classes they belong to. Using a Python dictionary, it's possible to parse the cecil.se file into a dictionary, using the exported class names as keys and a list of exported features as values. Half our problem is now solved--simply iterate through this dictionary and, for each key, create a python shadow class--for each item in that key's list of values, create the appropriate function by stripping off the first identifier before an underscore (for example, the exported feature test_print_integer_addition would be exported as the function print_integer_addition).

Warning

Since the python shadow class feature name is based off the exported-to-C function name, Python shadow class function names may not match Eiffel class feature names--and it is possible to create Python shadow class function names which are identical, which could create problems. Be careful naming exported functions in the cecil.se file--you have been warned! Future versions of the make_python_shadow_classes.py script will base generated Python shadow class function names directly on the Eiffel class feature names, which will correct this problem.

So far, so good. But we still need to generate an argument signature for each Python shadow class function.

As it happens, this information is also located somewhere useful. When SWIG generates its glue code in C, it also generates a slim little "documentation" file--here, eiffel-glue_wrap.doc. The contents are not always very helpful--in fact, they simply list each exported function in its Python form with a short blurb about the return type--not terribly helpful for the human reader, but nice to know.

Example 11-10. The automatically-generated eiffel-glue_wrap.doc file

eiffel-glue_wrap.c

[ Python Module : eiffel_glue ]


initialize_eiffel_runtime(argc,argv)
        [ returns void  ]

cvar.eiffel_root_object
        [ Global : void * eiffel_root_object ]

test_print_integer_addition(C,a1,a2)
        [ returns void  ]

test_print_message(C)
        [ returns void  ]

double_array_to_external(C)
        [ returns void * ]

real_array_to_external(C)
        [ returns void * ]

integer_array_to_external(C)
        [ returns void * ]

character_array_to_external(C)
        [ returns void * ]

string_to_external(C)
        [ returns void * ]

string_from_external(C,a1)
        [ returns void  ]

In our case, though, that's great information--in fact, since each feature is listed on a line with its Python-compatible argument signature, we just have to do some cutting and pasting to get an argument signature for our new class functions (by removing the first argument, which represents the opaque pointer to the Eiffel object). The note on the return value is helpful, because there's no way to tell what the return type of a Python function is--it could return any Python object, so any information the user can get is helpful.

We now have three sources of information for automatic generation of Python shadow classes:

  1. The cecil.se file, which contains a list of exported classes, exported features, and the exported feature names

  2. The eiffel-glue_wrap.doc file, which contains Python-compatible type signatures and return types

  3. The eiffel_gluemodule.so file, which is the actualy binary shared library containing the Python module (which we can examine by importing it into Python).

The make_python_shadow_classes.py script takes all these sources of information and collates them into a single file, python_shadow_classes.py, which contains class definitions for all python shadow classes that are exported from Eiffel; with two relatively minor problems, using Eiffel from Python is almost simple.