Imports in __init__.py and 'import as' statement

You incorrectly assume that one cannot have an alias with from ... import, as from ... import ... as has been there since Python 2.0. The import ... as is the obscure syntax that not many know about, but which you use by accident in your code.

PEP 0221 claims that the following 2 are "effectively" the same:

  1. import foo.bar.bazaar as baz
  2. from foo.bar import bazaar as baz

The statement is not quite true in Python versions up to and including 3.6.x as evidenced by the corner case you met, namely if the required modules already exist in sys.modules but are yet uninitialized. The import ... as requires that the module foo.bar is injected in foo namespace as the attribute bar, in addition to being in sys.modules, whereas the from ... import ... as looks for foo.bar in sys.modules.

(Do note also that import foo.bar only ensures that the module foo.bar is in sys.modules and accessible as foo.bar, but might not be fully initialized yet.)

Changing the code as follows did the trick for me:

# import pkg.subpkg.two_longname as two
from pkg.subpkg import two_longname as two

And code runs perfectly on both Python 2 and Python 3.

Also, in one.py you cannot do from pkg import subpkg, for the same reason.


To demonstrate this bug further, fix your one.py as above, and add the following code in tst.py:

import pkg
import pkg.subpkg.two_longname as two

del pkg.subpkg

from pkg.subpkg import two_longname as two
import pkg.subpkg.two_longname as two

Only the last line crashes, because from ... import consults the sys.modules for pkg.subpkg and finds it there, whereas import ... as consults sys.modules for pkg and tries to find subpkg as an attribute in the pkg module. As we just had deleted that attribute, the last line fails with AttributeError: 'module' object has no attribute 'subpkg'.


As the import foo.bar as baz syntax is a bit obscure and adds more corner cases, and I have rarely if ever seen it being used, I would recommend avoiding it completely and favouring from .. import ... as.


Here is a theory on what's going on.

When you use the as reserved word, for instance:

import pkg.subpkg.two_longname as two

Python must to completely initialize and resolve all dependences that has to do with pkg.subpkg. But there is a problem, to completely load subpkg you need to completely load one.py as well right? wich at the same time imports two_longname.py using the as keyword ... Can you see the recursion here? That's why at the moment of doing:

import pkg.subpkg.two_longname as two

you get an error claiming subpkg does not exist.

To perform a test, go to one.py and change it to this:

#import pkg.subpkg.two_longname as two
from pkg.subpkg import two_longname

#class One(two.Two):
class One(two_longname.Two):
    pass

I suppose this is all about performance, Python loads a module partially whenever is possible. And the as keyword is one of the exceptions. I don't know if there are others, but it would be interesting know about them.