Reputation: 1220
I am using python 3.7. When using imp
, I noticed relative imports work differently depending on where the relative import happens.
For example, I have the following directory:
.
├── __init__.py
└── dir2
├── __init__.py
├── a.py
└── b.py
and file content as follows:
~/personal/dir1 via 🐍 v3.7.7 via C py372
❯ cat dir2/__init__.py
from .a import default
print("default is", default)
~/personal/dir1 via 🐍 v3.7.7 via C py372
❯ cat dir2/a.py
default = 12
~/personal/dir1 via 🐍 v3.7.7 via C py372
❯ cat dir2/b.py
from .a import default
print("default is", default)
~/personal/dir1 via 🐍 v3.7.7 via C py372
And I use imp.load_source
for both dir2/__init__.py
and dir2/b.py
, I got the following results:
❯ ipython
Python 3.7.7 (default, May 6 2020, 04:59:01)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.16.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import imp
/Users/user/anaconda3/envs/py372/bin/ipython:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
#!/Users/user/anaconda3/envs/py372/bin/python
In [2]: imp.load_source("m", "dir2/__init__.py")
default is 12
Out[2]: <module 'm' from 'dir2/__init__.py'>
In [3]: imp.load_source("m", "dir2/b.py")
---------------------------------------------------------
ImportError Traceback (most recent call last)
<ipython-input-3-40c4f63c0bb1> in <module>
----> 1 imp.load_source("m", "dir2/b.py")
~/anaconda3/envs/py372/lib/python3.7/imp.py in load_source(name, pathname, file)
167 spec = util.spec_from_file_location(name, pathname, loader=loader)
168 if name in sys.modules:
--> 169 module = _exec(spec, sys.modules[name])
170 else:
171 module = _load(spec)
~/anaconda3/envs/py372/lib/python3.7/importlib/_bootstrap.py in _exec(spec, module)
~/anaconda3/envs/py372/lib/python3.7/importlib/_bootstrap_external.py in exec_module(self, module)
~/anaconda3/envs/py372/lib/python3.7/importlib/_bootstrap.py in _call_with_frames_removed(f, *args, **kwds)
~/personal/dir1/dir2/b.py in <module>
----> 1 from .a import default
2 print("default is", default)
ImportError: attempted relative import with no known parent package
Why imp.load_source
behaves differently for __init__.py
versus other python file?
Upvotes: 0
Views: 415
Reputation: 280227
Relative imports are not a directory traversal mechanism. They are for importing other contents of the package in which they appear.
When you call imp.load_source("m", "dir2/__init__.py")
, Python thinks you're trying to load a package named m
whose __init__.py
is dir2/__init__.py
. In this context, relative imports are relative to the package m
, and they search for submodules in directory dir2
.
When you call imp.load_source("m", "dir2/b.py")
, Python thinks you're trying to load a top-level module named m
. There is no package for relative imports to be relative to, and relative imports are meaningless in this context.
Upvotes: 1