This question already has answers here:
Relative imports for the billionth time
(12 answers)
Closed 2 years ago.
I can't for the life of me get python's relative imports to work. I have created a simple example of where it does not function:
The directory structure is:
__init__.py
start.py
parent.py
sub/
__init__.py
relative.py
/start.py contains just: import sub.relative
/sub/relative.py contains just from .. import parent
All other files are blank.
When executing the following on the command line:
$ cd /
$ python start.py
I get:
Traceback (most recent call last):
File "start.py", line 1, in <module>
import sub.relative
File "/home/cvondrick/sandbox/sub/relative.py", line 1, in <module>
from .. import parent
ValueError: Attempted relative import beyond toplevel package
I am using Python 2.6. Why is this the case? How do I make this sandbox example work?
You are importing from package "sub". start.py is not itself in a package even if there is a __init__.py present.
You would need to start your program from one directory over parent.py:
./start.py
./pkg/__init__.py
./pkg/parent.py
./pkg/sub/__init__.py
./pkg/sub/relative.py
With start.py:
import pkg.sub.relative
Now pkg is the top level package and your relative import should work.
If you want to stick with your current layout you can just use import parent. Because you use start.py to launch your interpreter, the directory where start.py is located is in your python path. parent.py lives there as a separate module.
You can also safely delete the top level __init__.py, if you don't import anything into a script further up the directory tree.
If you are going to call relative.py directly and i.e. if you really want to import from a top level module you have to explicitly add it to the sys.path list.
Here is how it should work:
# Add this line to the beginning of relative.py file
import sys
sys.path.append('..')
# Now you can do imports from one directory top cause it is in the sys.path
import parent
# And even like this:
from parent import Parent
If you think the above can cause some kind of inconsistency you can use this instead:
sys.path.append(sys.path[0] + "/..")
sys.path[0] refers to the path that the entry point was ran from.
Checking it out in python3:
python -V
Python 3.6.5
Example1:
.
├── parent.py
├── start.py
└── sub
└── relative.py
- start.py
import sub.relative
- parent.py
print('Hello from parent.py')
- sub/relative.py
from .. import parent
If we run it like this(just to make sure PYTHONPATH is empty):
PYTHONPATH='' python3 start.py
Output:
Traceback (most recent call last):
File "start.py", line 1, in <module>
import sub.relative
File "/python-import-examples/so-example-v1/sub/relative.py", line 1, in <module>
from .. import parent
ValueError: attempted relative import beyond top-level package
If we change import in sub/relative.py
- sub/relative.py
import parent
If we run it like this:
PYTHONPATH='' python3 start.py
Output:
Hello from parent.py
Example2:
.
├── parent.py
└── sub
├── relative.py
└── start.py
- parent.py
print('Hello from parent.py')
- sub/relative.py
print('Hello from relative.py')
- sub/start.py
import relative
from .. import parent
Run it like:
PYTHONPATH='' python3 sub/start.py
Output:
Hello from relative.py
Traceback (most recent call last):
File "sub/start.py", line 2, in <module>
from .. import parent
ValueError: attempted relative import beyond top-level package
If we change import in sub/start.py:
- sub/start.py
import relative
import parent
Run it like:
PYTHONPATH='' python3 sub/start.py
Output:
Hello from relative.py
Traceback (most recent call last):
File "sub/start.py", line 3, in <module>
import parent
ModuleNotFoundError: No module named 'parent'
Run it like:
PYTHONPATH='.' python3 sub/start.py
Output:
Hello from relative.py
Hello from parent.py
Also it's better to use import from root folder, i.e.:
- sub/start.py
import sub.relative
import parent
Run it like:
PYTHONPATH='.' python3 sub/start.py
Output:
Hello from relative.py
Hello from parent.py
Related
I have a project I want to run on different machines without the need to modify the PYTHONPATH enviroment variable. My projects structure is as follows:
awesome_project/
data/
scripts/
__init__.py
predict/
importer/
__init__.py
__init__.py
predict.py
train/
importer/
__init__.py
__init__.py
train.py
utils/
__init__.py
configuration.py
In my predict and train code I need to import variables defined in the configuration file inside utils. In Python 2 I defined the importer module, in which the __init__.py had the following code:
import sys
from os import getcwd
from os.path import sep
root_path = sep.join(getcwd().split(sep)[:-2])
sys.path.append(root_path)
And it worked as a charm. I imported the variables as: from scripts.utils.configuration import models_path, but now I'm migrating my code to Python 3 and this does not work at all, I get the following error:
Traceback (most recent call last):
File "predict.py", line 11, in <module>
from scripts.utils.configuration import models_path
ModuleNotFoundError: No module named 'scripts.utils'
What am I doing wrong?
I have read a ton of stackoverflow answers and a bunch of tutorials. In addition, I tried to read the Python documentation, but I cannot make this import work.
This is how the directory looks like:
myDirectory
├── __init__.py
├── LICENSE
├── project.py
├── README.md
├── stageManager.py
└── tests
├── __init__.py
└── test_project.py
There is a class in project.py called Project, and I want to import it in a file under tests directory. I have tried the following:
Relative import:
from ..project import Project
def print_sth():
print("something")
This gives me the following error: (running from the tests directory as python test_project.py and from myDirectory as python tests/test_project.py)
Traceback (most recent call last):
File "test_project.py", line 1, in <module>
from ..project import Project
SystemError: Parent module '' not loaded, cannot perform relative import
Absolute import with package name:
If I have something like the following, I get ImportError (with the same run command as above).
from project import Project
def print_sth():
print("something")
------------------------------------------------------
Traceback (most recent call last):
File "test_project.py", line 1, in <module>
from project import Project
ImportError: No module named 'project'
and this too:
from myDirectory.project import Project
def print_sth():
print("something")
------------------------------------------------------
Traceback (most recent call last):
File "test_project.py", line 1, in <module>
from myDirectory.project import Project
ImportError: No module named 'myDirectory'
Finally, I tried adding the if __name__ == '__main__' statement within the test_project.py file, but it still failed. I would really appreciate if anyone could help. If there is a solution where I do not have to write a verbose command, I would prefer that.
When you run a Python script by filename, the Python interpreter assumes that it is a top-level module (and it adds the directory the script is in to the module search path). If the script is in a package, that's not correct. Instead, you should run the module using the -m flag, which takes a module name in the same format as an import statement (dotted separators) and puts the current directory in the module search path.
So, you could run the test from myDirectory with: python -m tests.test_project. When you run the script this way, either of the kinds of imports you tried will work.
But if myDirectory is supposed to be a top-level package itself (as the __init__.py file suggests), you should probably go up one level further up, to myDirectory's parent, and run the script with two levels of package names: python -m myDirectory.tests.test_project. If you do this and want the test to use an absolute import you'd need to name the top level package that the project module is in: from myDirectory.project import Project.
I have the follwoing package structure
my-base-project
-> package1
__init__.py
MyScript.py
-> test
__init__.py
TestMyScript.py
I'd like to run the TestMyScript.py in the console. Therefore I cd in to my-base-project/test and execute python TestMyScript.py. However, I'm getting the error:
user#computer:~/my-base-project/test$ python TestMyScript.py
Traceback (most recent call last):
File "TestMyScript.py", line 4, in <module>
from package1 import MyScript
ImportError: No module named package1
How do I run these tests?
From this SO question, consider adding the directory you need to the PYTHONPATH:
import sys
sys.path.append('your certain directory')
Maybe you want to add the parent directory
sys.path.append('..')
Assume I have the following structure of a package:
parent/
package/
__init__.py
utils/
__init__.py
foo.py
apps/
__init__.py
bar.py
Now, I want to import the module foo from the module bar. What is the correct way to do it? I will welcome comments on the structure of my project :)
So far I only found answers where everything lives in the same directory... I tried something like:
from ..utils import foo
but I get:
Traceback (most recent call last):
File "./bar.py", line 4, in <module>
from ..utils import foo
ValueError: Attempted relative import in non-package
I think, it's better to set some environ variable or extend PYTHONPATH (it must be done in main script)
I have a package of the following form:
$ ls folder
entry_point.py hello.py __init__.py utils.py
This is a package, and I can treat it as such:
$ python2.7
>>> import folder.utils
>>>
I want to use relative imports between these Python modules.
$ cat folder/entry_point.py
from hello import say_hello
if __name__ == "__main__":
say_hello()
$ cat folder/hello.py
from .utils import say
def say_hello():
say()
$ cat folder/utils.py
def say():
print "hello world"
I know I can't use relative imports at the entry point, where I call the interpreter. However, I still get ImportError from my imported files:
$ python2.7 folder/entry_point.py
Traceback (most recent call last):
File "folder/entry_point.py", line 1, in <module>
from hello import say_hello
File "/tmp/folder/hello.py", line 1, in <module>
from .utils import say
ValueError: Attempted relative import in non-package
This is rather counterintuitive, it is a package, it's just not treated as one due to entry_point.py having __name__ set to __main__ (in line with PEP 328).
I'm surprised that hello.py has a __name__ of hello rather than folder.hello. This stops me using relative imports in hello.py.
How do I use relative imports in this package? Am I forced to move hello.py and utils.py to a libs subpackage?
If you want folder to be a module inside a bigger project and you want to be able to run entry_point.py for using your folder module - move entry_point.py one level up:
from folder.hello import say_hello
if __name__ == "__main__":
say_hello()
Import paths - the right way?