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 its __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__.py
and 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__.py
is to import selected Classes, functions, etc into the package level so they can be conveniently imported from the package.Changing
import as
tofrom import
inone.py
:from pkg.subpkg import two_longname class One(two_longname.Two): pass
The 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__.py
and usage ofimport as
leads 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).