I have a very simple namespace package (contents included below, as well as the directory layout). If I try to import namespace_repro.module
, I got the following error: AttributeError: module 'namespace_repro' has no attribute 'module'
. As far as I understand my package has a valid layout and the import should work. Interestingly, the error is present only in Python 3.6.8 and the import succeeds in Python 3.7.
I have a directory named import-error-repro
with in it a setup.py
(see below), then a nested directory path src/namespace_repro/module
, containing three files, __init__.py
, x.py
and y.py
. Their contents:
setup.py
from setuptools import find_namespace_packages, setup
setup(
name='namespace-repro',
version='0.1.0',
python_requires='>=3.6',
packages=find_namespace_packages('src'),
package_dir={'': 'src'},
zip_safe=False,
)
src/namespace_repro/module/__init__.py
:
from namespace_repro.module.x import x
src/namespace_repro/module/x.py
:
import namespace_repro.module.y as y
x = y.y
and finally src/namespace_repro/module/y.py
:
y = True
I created a brand new Python 3.6 conda environment by conda create -n namespace6 python=3.6 ipython
, then I activated it and installed the package as pip install -e ./import-error-repro
(note that -e
doesn't matter, the problem is reproducible without it). After that, I tried import namespace_repro.module
in ipython
(though the same happens in the official python interpreter). The result is
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-1-bcae5a697dad> in <module>
----> 1 import namespace_repro.module
~/namespace-repro/src/namespace_repro/module/__init__.py in <module>
----> 1 from namespace_repro.module.x import x
~/namespace-repro/src/namespace_repro/module/x.py in <module>
----> 1 import namespace_repro.module.y as y
2
3 x = y.y
AttributeError: module 'namespace_repro' has no attribute 'module'
---------------------------------------------------------------------------
The strange thing is that the import system finds namespace_repro.module
twice but fails at the third time!
Some other interesting behaviour:
In [1]: import namespace_repro.module.y as y # This doesn't work.
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-4035347ea59b> in <module>
----> 1 import namespace_repro.module.y as y
AttributeError: module 'namespace_repro' has no attribute 'module'
In [2]: import namespace_repro.module.y # But this one does! Why?
In [3]: dir(namespace_repro.module.y) # The error returns when we actually want to use the module.
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-d89bcfd9e509> in <module>
----> 1 dir(namespace_repro.module.y)
AttributeError: module 'namespace_repro' has no attribute 'module'
In [4]: from namespace_repro.module.y import y # This works fine!
In [5]: y
Out[5]: True
. import-error-repro
+-- setup.py
+-- src
| +-- namespace_repro
| | +-- module
| | | +-- __init__.py
| | | +-- x.py
| | | +-- y.py
If the error occurs due to a circular dependency, it can be resolved by moving the imported classes to a third file and importing them from this file. If the error occurs due to a misspelled name, the name of the class in the Python file should be verified and corrected.
The __init__.py files are required to make Python treat directories containing the file as packages. This prevents directories with a common name, such as string , unintentionally hiding valid modules that occur later on the module search path.
When resolving a relative module, Python first looks for a folder with that name and its __init__.py . If that exists, it will extract the variable from the __init__.py as in import x . If it does not exist, it will look for world.py at the same level as the import. If that is not found, an Exception will be raised.
This is CPython bug 30024, which unsurprisingly was fixed in 3.7. Note that the more idiomatic form with relative (circular) imports has worked since 3.5.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With