I have the script I want to run in the following structure
In main.py I need to import things from a.py. How can I import things in subfolders that are two or more folders above main.py?
The proper way to handle this would be putting everything that needs to know about each other under a shared package, then the individual sub-packages and sub-modules can be accessed through that package. But this will also require moving the application’s entrypoint to either the package, or a module that’s a sibling of the package in the directory and can import it. If moving the entrypoint is an issue, or something quick and dirty is required for prototyping, Python also implements a couple other methods for affecting where imports search for modules which can be found near the end of the answer.
For the package approach, let’s say you have this structure and want to import something between the two modules:
. ├── bar_dir │ └── bar.py └── foo_dir └── foo.py
Currently, the two packages do not know about each other because Python only adds the entrypoint file’s parent (either
foo_dir depending on which file you run) to the import search path, so we have to tell them about each other in some way, this is done through the top level package they’ll both share.
. └── top_level ├── __init__.py ├── bar_dir │ ├── __init__.py │ └── bar.py └── foo_dir ├── __init__.py └── foo.py
This is the package layout we need, but to be able to use the package in imports, it has to be initialized, which means we either need to import the
top_level package (e.g. from a
main.py entrypoint), or define it as a runnable module by implementing a
__main__.py file inside of it, next to
__init__.py, which is ran when doing
python -m top_level.
This entrypoint module would then run the module which you were running beforehand.
__init__.py files are used to mark the directories as proper packages and are ran when the package is imported to initialize its namespace.
With this done the packages now can see each other and can be accessed through either absolute or relative imports, an absolute import would being with the
top_level package and use the whole dotted path to the module/package we need to import, e.g.
from top_level.bar_dir import bar can be used to import
Packages also allow relative imports which are a special form of a from-style import that begins with one or more dots, where each dot means the import goes up one package – from the
from . import module would attempt to import
module from the
from .. import module would search for it in the
top_level package etc.
One thing to note is that importing a package doesn’t initialize the modules under it unless it’s an explicit import of that module, for example only importing
top_level won’t make
bar_dir available in its namespace unless they’re imported directly through import
top_level.bar_dir or the package’s
__init__.py added them to the package’s namespace through its own import.
If this doesn’t work in your structure, an another way is to let Python know where to search for your modules by adding to its module search path, this can be done either at runtime by inserting path strings into the
sys.path list, or through the PYTHONPATH environment variable.
Continuing with the above example with a scenario and importing
foo, an entry for the
bar_dir directory (or the directory above it) can be added to the
sys.path list or the aforementioned environment variable. After that
import bar (or
from bar_dir import bar if the parent was added) can be used to import the module, just as if they were next to each other. The inserted path can also be relative, but that is prone to breakage with a changing cwd.