I've run into a problem with having imports in __init__.py and using import as with absolute imports in modules of the package.
My project has a subpackage and in it's __init__.py I "lift" one of the classes from a module to the subpackage level with from import as statement. The module imports other modules from that subpackage with absolute imports. I get this error AttributeError: 'module' object has no attribute 'subpkg'.
Example
Structure:
pkg/
├── __init__.py
├── subpkg
│ ├── __init__.py
│ ├── one.py
│ └── two_longname.py
└── tst.py
pkg/__init__.py is empty.
pkg/subpkg/__init__.py:
from pkg.subpkg.one import One
pkg/subpkg/one.py:
import pkg.subpkg.two_longname as two
class One(two.Two):
pass
pkg/subpkg/two_longname.py:
class Two:
pass
pkg/tst.py:
from pkg.subpkg import One
print(One)
Output:
$ python3.4 -m pkg.tst
Traceback (most recent call last):
File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.4/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/and/dev/test/python/imptest2/pkg/tst.py", line 1, in <module>
from pkg.subpkg import One
File "/home/and/dev/test/python/imptest2/pkg/subpkg/__init__.py", line 1, in <module>
from pkg.subpkg.one import One
File "/home/and/dev/test/python/imptest2/pkg/subpkg/one.py", line 1, in <module>
import pkg.subpkg.two_longname as two
AttributeError: 'module' object has no attribute 'subpkg'
Workarounds
There are changes that make it work:
Empty
pkg/subpkg/__init__.pyand importing directly frompkg.subpkg.one.I don't consider this as an option because AFAIK "lifting" things to the package level is ok. Here is quote from an article:
One common thing to do in your
__init__.pyis to import selected Classes, functions, etc into the package level so they can be conveniently imported from the package.Changing
import astofrom importinone.py:from pkg.subpkg import two_longname class One(two_longname.Two): passThe only con here is that I can't create a short alias for module. I got that idea from @begueradj's answer.
It is also possible to use a relative import in one.py to fix the problem. But I think it's just a variation of workaround #2.
Questions
Can someone explain what is actually going on here? Why a combination of imports in
__init__.pyand usage ofimport asleads to such problems?Are there any better workarounds?
Original example
This is my original example. It's not very realistic but I'm not deleting it so @begueradj's answer still makes sense.
pkg/__init__.py is empty.
pkg/subpkg/__init__.py:
from pkg.subpkg.one import ONE
pkg/subpkg/one.py:
import pkg.subpkg.two
ONE = pkg.subpkg.two.TWO
pkg/subpkg/two.py:
TWO = 2
pkg/tst.py:
from pkg.subpkg import ONE
Output:
$ python3.4 -m pkg.tst
Traceback (most recent call last):
File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.4/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/and/dev/test/python/imptest/pkg/tst.py", line 1, in <module>
from pkg.subpkg import ONE
File "/home/and/dev/test/python/imptest/pkg/subpkg/__init__.py", line 2, in <module>
from pkg.subpkg.one import ONE
File "/home/and/dev/test/python/imptest/pkg/subpkg/one.py", line 6, in <module>
ONE = pkg.subpkg.two.TWO
AttributeError: 'module' object has no attribute 'subpkg'
Initially I had this in one.py:
import pkg.subpkg.two as two
ONE = two.TWO
In that case I get error on import (just like in my original project which uses import as too).
import pkg.subpkg.SOMETHINGorfrom pkg.subpkg import SOMETHINGfrom WHEREVER you want, you will get the same error. I reedited my answer: I changed only the structure of your program, mainly tst.py is outside pkg (in fact, i run it this way since the first time i posted my answer below). If you WANT to keep tst.py inside pkg then removepgkfrom all your calls likeimport pkg.subpkg.SOMETHINGandfrom pkg.subpkg import SOMETHING- user3522371tst.pyis inside the package, that's the case. What do you mean by "removepkgall your calls..."? Relative imports? I know that it would work with relative imports. The question is why it doesn't work the way it is. - andimport subpkg.SOMETHINGinstead ofimport pkg.subpkg.SOMETHINGandfrom subpkg import SOMETHINGinstead offrom pkg.subpkg import SOMETHING. Why ? Well: you can not run your program with invalid paths. As simple as this. - user3522371