Explanation
This is a problem I encouter a lot when using Python imports. First, let's take a look at sys.path
when we run the program. As far as I know, this lists the folders your Python executable will look into when trying to import a module.
one.py
import sys
print(sys.path)
Now, let's run one.py
from the root directory of your project (the folder containing root/
and main/
). NB: I am using Windows, so the path formatting is a bit different from Unix systems.
console
$ python ./main/one.py
['C:\\Users\\spaceburger\\Downloads\\example\\main', ...]
Now we know Python knows the location of the main/
folder. However, it doesn't know it can go back to the example/
folder (even if we use from ..root import new
, don't ask me why...). However you should be able to import other modules from the main/
folder quite easily. Let's check this out :
two.py
print("two")
one.py
import two
console
$ python ./main/one.py
two
You can also import from subfolders in of the main/
folder if you create one. E.g. after creating a file main/submain/three.py
, you can use from submain import three
kind of statements.
However, what we want is to import from a folder that is higher in the tree. To do so, I see two options :
Option 1: change your path
The idea is simple : since Python doesn't know the root/
folder, we just have to declare it. The idea is simple but this is not something I would recommend to do because it might make your code messy. But sometimes, this is the only way !
For that, you can add the following statements to one.py
but the preferred method would be to create an __init__.py
file in the main/
folder and to add the following :
import sys, os
rel_path = os.path.join(os.path.dirname(__file__), "..") # if we use just "..", the path will be based on cwd (which is my download/ folder but might also be anything else)
abs_path = os.path.abspath(rel_path)
sys.path.insert(1, abs_path)
print(sys.path)
Now you can re-write one.py
from root import new
Should work fine (note : from root
and not from ..root
). In fact, you can import from every folder in the example/
folder now.
If you replace os.path.join(os.path.dirname(__file__), "..")
by os.path.join(os.path.dirname(__file__), "..", "root")
, then you can directly import modules from the root/
folder as if your one.py
was just next to your new.py
. I.e. you should use import new
in that situation.
Option 2: change the entry point of your program
The other way to add example/
or root/
to the Python path is to launch your program directly from there !
Create an entry.py
file in the example/
folder:
entry.py
from main import one
one.do_something_with_new()
And in one.py
you can assume the example/
folder is known to Python and that you can import from evey subfolder there.
one.py
from root import new
def do_something_with_new():
new.do_something()
As long as you always start your program with entry.py
. Be careful with the name of your files, I would usually use app.py
or main.py
in stead of entry.py
but it might cause confusion with your folder names (for python as well). I don't think new.py
is recommended as well.
Conclusion
You can either set the entry point of your program in a folder that contains all the subfolders you wish to use and use import statements like from subfolder.subsubfolder import xxx
, or you can modify your Python path and add the folders you wish to use in to the list of known locations that way you can use import statements such as import xxx
directly.
However I do not recommend this last option because you might get conflicts with the name of your subfolders (if you have a main/A.py
and a root/A.py
, then import A
will load the first one that is found, which depends on the order of your folders paths in your Python path variable). Also, this takes a few lines of code to write, whereas you can get a result just as nice just with some organization.
Disclaimer
I usually use one of the two options above because I do not know of any other. This is all based on my experience and reading StackOverflow posts, not on the documentation or anything like that, so please take my advice with a grain of salt.