I’ve created a package, zope.fixers, that will contain the 2to3 fixers needed to port Zope and Zope Component Architecture code to Python 3. If you want the gritty details, read on.
I want the Component Architecture to be available under Python 3, so I have tried porting it. To do that I needed setuptools, which is why I have ported setuptools to Python 3. But when it came to looking into porting zope.interface again, I noticed that the normal build process uses zc.buildout and zope.testing. Both of which depends on zope.interfaces. So there was a bit of a chicken and egg problem there.
So I decided to look into this more deeply during the PyCon sprint, as Jim Fulton was there and could help me set up a build environment that could work. However, when I looked more deeply into it, I realized I could set up a way to run the zope.interfaces tests with setuptools instead of with zope.testing and thereby bypass the need for both zope.testing and zc.buildout. Said and done. There is now a regebro-python3 branch of zope.interface where I have commenced doing Python 3 support.
The big problem with zope.interface
Currently you mark a class as implementing an interface with the implements() method.
class Foo: implements(IFoo)
This syntax works by hacking the locals namespace that gets passed into the class construction, by inserting a __metaclass__ variable that uses a so called “meta class advisor”. This way zope.interface gets a hook into the class construction and can during the class construction mark the class ass implementing the interface.
Yeah, that’s some serious Python-internal magic there. And this magic does not work under Python 3, since the __metaclass__ is now ignored. However, there is a new and much cleaner way of modifying and annotating classes, and that’s class decorators. So, for Python 3, zope.interfaces will use class decorators instead of the class body function calls currently used.
@implementer(IFoo) class Foo: pass
This will also work under Python 2.6 and 2.7, allowing smooth transitions.
However, most people will need this to be changed automatically when they port their software to Python 3. Therefore, we need a fixer to move from the old syntax to the new. And I needed a place to put those fixers. That place is zope.fixers, which currently only contain one fixer, the fix_implements, that does the above change. zope.fixers will be released in v1.0 as soon as I get all zope.interface tests to run. This will probably take some time, as the sprint is long over, and my free time has ran out. People are very welcome to help port zope.interface and write more fixers (I think some more will be needed).
Writing fixers that actually move code around like this turns out to be damn hard, which is why it took me so long. But that’s another blogpost.