Search code examples
pythonpackaging

Understanding the package structure (Module X.Y has no attribute Z)


This is the tree:

X
|_ setup.py
|_ X
   |_ someFile.py
   |_ __init__.py (empty)
   |_ Y
      |_ __init__.py (empty)
      |_ anotherFile.py

After running pip install -e ., I can run the following commands anywhere on my system:

import X
from X import someFile
from X.Y import anotherFile

But I cannot run:

from X import Y
Y.anotherFile

-> Module X.Y has no attribute "anotherFile".

I can fix that by filling the lower-level __init__.py with from X.Y import anotherFile, but that seems a bit odd.

Why is that? Is my understanding of python packages wrong?

Disclaimer: Someone might have asked this before, but the question is so generic that I had difficulties finding the right post.


Solution

  • It's simple actually. For from X import Y, the imported module is the __init__.py file in directory Y but not all other files.

    But I think the real question is, Why?

    Let's say anotherFile is the only module you want, so you import anotherFile by from X.Y import anotherFile. But actually you have also imported X and Y. Although you cannot access them by X or Y, but if you import sys and sys.modules.keys(), you can see these modules as X and X.Y. And you can access the attributes in Y by sys.modules["X.Y"].a # suppose there is. That means the __init__.py file under directory Y has been executed.

    OK, so now if importing Y or X will also execute all files under that package, guess what? Yes, even though you just need anotherFile module, all files including directory Y under directory X will be executed. And these files also import other files and so on... With just one import, the whole project is imported.