Sunday, August 5, 2007

Python Import Hell

Coming from a Java background to Python about two years ago, I suppose I confused Python packages and modules a bit (compare Java's packages and classes - it's a different thing!). Anyway, gPodder's current module and package structure is a bit uncoordinated, partly because I started out with the module layout early in my Python carreer and partly because I sometimes added classes where they shouldn't be placed, resulting in circular imports. I somehow managed to fix all import errors and it runs for quite a while with that layout now.

After Pierre-Luc Beaudoin informed me that his Sofa Media Center now uses gPodder as its podcast client/backend, and after looking at Pierre-Luc's code, I thought it would be good to provide some kind of gPodder API for other developers to build upon the gPodder functionality. This way, gPodder would be a modular app with a GUI, but it could also function as simple podcast client library (if needed).

So, yesterday, I started to restructure the gPodder package/module layout a bit. While doing this, I had some problems with circular imports. The problem was that I had a "gpodder" module in the "gpodder" package, and Python 2.4's "import" statement first searches the current package and then the "global" package/module path. I read about Python's built-in Package support that is available since Python 1.5. Great! But it already mentions that there could be problems with relative imports. Another article entited Importing Python Modules then explained the partly confusing import hell of Python. The article says one should "always use import X" (except for some special cases).

Circular imports can often easily be avoided by placing depended-on code in seperate modules. It's also good to see that this problem is partly solved by PEP 328, which is (optionally) available starting with python 2.5, throws a warning and 2.6 and will be the default in Python 2.7: Absolute imports. This way, naming a package-local module like a top-level package will not result in confusion. One can either use "import X" to do an absolute import or do "from . import X" to do a relative import. Currently (Python 2.4), "import X" will prefer the local module over a global package/module with the same name. Confusing, isn't it?

No comments: