The Python Import Name Game

While working on Nexus, my current project, I ran into a really strange error. I had just finished rewriting all of the database models I was planning to use and wanted to do a basic consistency check. The structure of the package was as follows:

nexus/
    models/
        db.py       # SQLAlchemy Base and misc
        feeds.py    # Models related to RSS feeds
        nexus.py    # Generic models
        users.py    # User-related models
        ...         # lots of other models
    ui/
        views/
            subscribe.py   # View for subscriptions

I wrote the subscription code as a test of the models, since it had to touch most of them in order to get a user subscribed to a feed. When I wrote a script to try executing the subscription code I found that it had an import error loading nexus.models.db. It seemed strange, so I opened up an interpreter and tried doing some imports:

>>> import nexus
>>> import nexus.db
>>> import nexus.ui.views.subscribe
Traceback (most recent call last):  
  File "<stdin>", line 1, in <module>
  File "nexus/ui/views/subscribe.py", line 2, in <module>
    import nexus.models.feeds as feeds
  File "nexus/models/feeds.py", line 6, in <module>
    from nexus.models.db import Base
  File "nexus/models/nexus.py", line 14, in <module>
    from nexus.models.db import Base
ImportError: No module named db  

At first I'd been assuming that I had a circular reference somewhere, but when I looked there definitely wasn't on. That was cemented by the fact that I had already imported the module that it couldn't find. The bigger mystery, which turned out to be the key to the whole thing, was why feeds.py was importing nexus.py on the line where it should have been importing nexus.models.db. I spent a while investigating assuming that the stack trace was somehow missing something because python imports are a really complicated sub-system. I tried deleting the .pyc files since I've had problems with old pyc files from now-deleted py files causing issues. I tried deleting every file in the project except the ones necessary for the code to run, and as a last ditch effort after reading this import-related bug report I tried changing all of my from X import Y style imports to import X.Y as Y, but no luck.

What turned out to be the problem was that from nexus.models.db import Base was interpreted as a relative import inside of the nexus.models package. nexus.models.nexus does not have an attribute called db which is a model that could be imported, so the import fails.

Lesson Learned: Do not name a python module the same thing as the root package, especially if you're planning to import that module from within the same package.