Date: | 9 December 2008 |
---|---|
Tags: | computing, grok, pyephem, python, zope |
With several packages already advertising Python 3.0 compatibility, it seemed high time to look into releasing my PyEphem astronomy package in an edition compatible with the new language. But I hesitated: how difficult is it really, and how many hours of work will it consume, to port a C-language extension module to Python 3.0?
The answer is that, while the necessary changes were surprisingly easy, they took lots of time to figure out because I did not find them documented in any one place. I offer the following notes to assist any other adventurers who want to experiment with porting their extension modules to 3.0. These notes might also suggest useful additions to the official documentation.
But, first, I need to issue three cautions. To develop under 3.0, you may have to forego several Python tools that you probably thought you could no longer do without. The world of 3.0 is a windswept and icy landscape from which the glaciers have just receded, and you will find the stone tools rather primitive when compared to the comforts of civilization that you enjoy under Python 2. To wit:
$ python setup.py test
So if you make the effort to port your code right now, you might find that the shiny new version of your module is all dressed up, but has no place to go. If you experiment with the following steps, though, you will at least be ready when an official distribution channel does appear for releasing your package into the wilds of 3.0.
Yes, four steps were all that were necessary to convert my quite complex extension module to Python 3.0!
Adjust all Python object headers. Each type object in my code started with a macro to set up the common fields that all Python objects share. This was then followed by the ob_size field, which in my code always is always zero, and then the type name:
/* For Python 2 */
static PyTypeObject BinaryStarType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"ephem.BinaryStar", /* tp_name */
...
Though the Python 3.0 documentation still shows this as the way to create types, this technique will now completely fail. (The bug indicating that the documentation gets this wrong has, as its most recent comment, the helpful note “I'm lowering the priority so it doesn't block the release.”) Anyway, the solution is simple: the first two lines in the struct shown above simply have to be combined into a single macro call:
/* For Python 3.0 */
static PyTypeObject BinaryStarType = {
PyVarObject_HEAD_INIT(NULL, 0)
"ephem.BinaryStar", /* tp_name */
...
With this change, my objects are now operating fine.
When migrating the PyEphem code base, I found that most of the Unicode transition was very easy. Everywhere that my code handled or created a string object, I simply changed the prefix of the function to PyUnicode and everything worked:
PyString_Check ... becomes ... PyUnicode_Check
PyString_FromString ... becomes ... PyUnicode_FromString
PyString_FromFormat ... becomes ... PyUnicode_FromFormat
Well, okay, the trick does not work everywhere; this one was harder to guess:
PyString_Size ... becomes ... PyUnicode_GET_SIZE
The situations that require real thought are the places where my code needs to convert a Python string into the sort of simple ASCII character array that the underlying C library can absorb. At the moment, my code is leaning heavily on a pitiful PyUnicode_AsString() routine that I wrote just to get things working; in the morning I will have to look into doing this more correctly, including catching the error if a fancy Unicode character is present that cannot properly be converted.
Overall, I am very impressed with how quickly I was able to get my extension module compiling and running under Python 3.0. The procedure was simple — I just tried, over and over again, to build the module with:
$ python3.0 setup.py build
and then tackled the compiler errors that resulted. Once every last warning had been addressed, the module started up and operated without a single further complaint. This calls for celebration! I'm going to bed.