Hi. I'm also an upstream developer on both Twisted and Nevow. Chris just explained what's going on to me and I think it might be helpful in resolving this issue if I explained the twisted plugin system's motivation and design in a bit more depth.

Basically, the idea behind the Twisted plugin system is simple: allow applications to inspect the importable modules in a particular package, and load objects which provide a particular interface from those modules. We tried as hard as possible not to change the rules of regular Python importing.

However, a completely literal interpretation of Python imports made development installations difficult to work with, because one needed to copy plugin files from the development installation of a project (say, Nevow) to the working Twisted installation every time they changed - otherwise, you wouldn't be able to import the appropriate module. So we added a tiny bit of extra logic to twisted.plugins' __init__.py to allow dependent projects to have their own "twisted/plugin" directory, and allow another mechanism (virtual-python, combinator, PYTHONPATH in .bashrc) to control sys.path.

This was originally a very naive expression which would set __path__ on twisted.plugins to allow importing from every "twisted/plugins" directory on sys.path. However, this was a mistake, because it meant that a developer who had specifically configured their PYTHONPATH to pick up a Twisted installation in their home directory would suddenly find modules coming from twisted.plugins in their system install. This would mix together incompatible versions of Twisted, breaking otherwise working code. The change that Chris described above fixes this bug, by only including "twisted/plugin" directories on sys.path which specifically are not packages, like those which are present in projects which provide Twisted plugins (Axiom, Nevow, and MV3D, to name a few). These projects specifically and intentionally omit __init__.py files, because the intent is that when they are installed, they should not overwrite the twisted/__init__.py or twisted/plugins/__init__.py installed by Twisted. As it happens, neither of these files are empty and overwriting them would break a Twisted installation.

Back to the original intent of this plugin system: it is to allow applications to interrogate modules which can be loaded using Python's normal 'import' semantics. Without the aforementioned __path__ magic (which is provided for developers' convenience who are trying *not* to touch the system install, and should not be relied upon by a system like Debian, which controls the system install), if you have a /usr/lib/python2.5/site-packages/mypackage/foo.py and a /var/lib/python- support/python2.5/mypackage/bar.py (each next to its own __init__.py), only mypackage.foo will be importable - mypackage.bar will be effectively invisible to Python.

In my opinion, python-support should not be providing multiple conflicting packages on the default sys.path regardless of this particular issue. Obviously, this makes it more pressing, but multiple things with the same name, disambiguated only by the ordering of sys.path, can cause confusion for any code, especially naive code, which attempts to interrogate sys.path. It causes confusion for users who are looking at a directory listing in one window and an ImportError in another. Consider this:

$ mkdir -p a/x
$ mkdir -p b/x
$ touch a/x/__init__.py
$ touch b/x/__init__.py
$ touch b/x/y.py
$ PYTHONPATH=a:b python -c 'import x.y'
Traceback (most recent call last):
 File "<string>", line 1, in <module>
ImportError: No module named y

There's no information available for the user to determine that the "x" package is coming from the "a" entry on sys.path here; the error is very difficult to debug, even more so if it stems from a bug in a system- installed package.

I hope this clearly illustrates why Twisted works this way. I believe that the correct solution here is neither to remove the auto-generated __init__.py nor to re-introduce the bug described above that we fixed upstream in Twisted. Happily, there is a unit-test for this bug, so if you do need to manipulate __path__ somehow, you can verify that it's a supported way by running the twisted test suite.

To be honest I do not fully understand the purpose of python-support; in particular, the addition of /var/lib/python-support/pythonX.X to sys.path seems to create a lot of problems like this one and I don't see what issues it solves. There are already tons of symlinks here; why not just symlink everything in /var/lib into /usr/lib appropriately?

However, assuming there is a good reason for the addition of this path entry, once a location has been selected for a given python package (i.e. "/usr/lib/python2.5/site-packages" for "twisted.plugins"), every other Debian package that intends to place files into that Python package really needs to follow suit and put its own files there, or resort to unpleasant and potentially confusing workarounds to makes the available modules be importable.


Reply via email to