This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Strange behavior when importing internal modules in the __init__.py of a submodule
Type: behavior Stage:
Components: Interpreter Core Versions: Python 3.3, Python 3.4, Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: antoine.pietri, brett.cannon
Priority: normal Keywords:

Created on 2013-06-05 19:17 by antoine.pietri, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
lolpython.tar.xz antoine.pietri, 2013-06-05 19:17
Messages (4)
msg190688 - (view) Author: Antoine Pietri (antoine.pietri) * Date: 2013-06-05 19:17
I just found a very strange bug today, and it took me like two hours to figure out the problem.

We first create a package "package", which contains an __init__.py, which makes an absolute import of package/foo.py (import package.foo), which makes an absolute import of package/bar.py (import package.bar).
Everything works fine as expected, the modules are imported correctly in the __init__.py and we can use them.

Now, if we move everything in a subpackage, the behavior is complete nonsense. We then have package/subpackage/{foo,bar,__init__}.py and an empty package/__init__.py.
We can import package.subpackage.foo and use it, but when we import package.subpackage.bar, the "import" statement works as expected but we CAN'T use the imported package:

>>> import package.subpackage.bar  # works fine
>>> dir(package.subpackage.bar)  # WAT
AttributeError: 'module' object has no attribute 'subpackage'

You can find a tarball attached to this bug report that contains the working case and the failing case:

package1
├── bar.py
├── foo.py
└── __init__.py

package2
└── subpackage
    ├── bar.py
    ├── foo.py
    └── __init__.py

$ python3
>>> import package1.foo
__init__: importing package1.foo
    foo.py: importing package1.bar
    foo.py: package1.bar.__name__: package1.bar
__init__: package1.foo.__name__: package1.foo
>>> import package2.subpackage.foo
__init__: importing package2.subpackage.foo
    foo.py: importing package2.subpackage.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./package2/subpackage/__init__.py", line 2, in <module>
    import package2.subpackage.foo
  File "./package2/subpackage/foo.py", line 3, in <module>
    print('    foo.py: package2.subpackage.bar.__name__:', package2.subpackage.bar.__name__)
AttributeError: 'module' object has no attribute 'subpackage'


tl;dr: you can use only relative imports to refer to modules of a package inside a module imported by the __init__.py of this package  except if the package is not a subpackage. Else, the package will be successfully imported but trying to use it will lead to a weird AttributeError. (Wat.)
msg190689 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2013-06-05 20:11
It's because you have a nested circular import. When you import package2.subpackage from within package2.subpackage you're in package2 importing package2 and also in package2.subpackage importing package2.subpackage.

You can solve your problem by doing either ``from package2.subpackage import foo`` for ``from . import foo`` as that lets package2.subpackage be imported entirely on its own before attempting package2.subpackage.foo and thus letting the circular loop unroll and have the right attributes set since the attributes of a module for a package are set after the import completes.

Might be annoying, but tweaking this would probably break code if changed as it's very old semantics to set the attribute of a module on a package after other imports complete. This is also not a problem as long as you don't do this in an __init__ (e.g. importing package2.subpackage.bar from package2.subpackage.foo is not a problem).
msg190691 - (view) Author: Antoine Pietri (antoine.pietri) * Date: 2013-06-05 20:20
Okay, maybe my message was unclear. I figured out on my own how to work around this issue (for instance using relative imports), the main problem for me is that this issue is very difficult to debug. The "import" statement should at least fail with a more explicit error, imho.
msg190694 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2013-06-05 21:39
It can't. The AttributeError is in the executing module code, not import itself so there is no reasonable way to tell that the failure was because of a circular loop in the import and not simply because you tried to get at an attribute that doesn't exist for some other reason.
History
Date User Action Args
2022-04-11 14:57:46adminsetgithub: 62345
2013-06-05 21:39:29brett.cannonsetstatus: open -> closed
resolution: not a bug
messages: + msg190694
2013-06-05 20:20:53antoine.pietrisetstatus: closed -> open
resolution: wont fix -> (no value)
messages: + msg190691
2013-06-05 20:11:43brett.cannonsetstatus: open -> closed

nosy: + brett.cannon
messages: + msg190689

resolution: wont fix
2013-06-05 19:52:33brett.cannonsetversions: + Python 2.7, Python 3.4
2013-06-05 19:17:53antoine.pietricreate