Coming from a static, strongly-typed language such as C# or Java, it was just a normal thing for me to reference other projects and custom types within those projects. No big deal.
But when I started using Python and wanted to import from parent directory, python would not allow it. While searching for a solution, I came to find out that in Python, importing a module from a parent or sibling directory is a different story altogether.
This post addresses this issue and proposes a solution.

The Problem
Suppose this is the directory structure of a project:

Our script to run is run.py
, which will reference common/a.py
.
a.py
simply contains a single function:
def CommonFunction():
print("This is where a gets called")
Now in run.py
, we simply try to import a.py
:
from common import a
print("Execution of run starts")
a.CommonFunction()
print("Execution of run finishes")
We run the script in Visual Studio Code by right clicking the script and choosing the option:

Or run from the terminal:
python3 run.py
You are going to get the following error:
ModuleNotFoundError: No module named 'common'
Explanation
Starting from Python 3.3, implicit relative references are allowed no more. That means that the ability to reference a module in the parent directory is not possible and becomes a major limitation.
So in order to reference a module, the directory that contains a module must be present on PYTHONPATH – PYTHONPATH is an environment variable which contains the list of packages that will be loaded by Python upon execution. What is in PYTHONPATH is also present in sys.path
as well.
The directory from where a script or module is run is automatically added to PYTHONPATH. You can import any module that lies within this directory, but modules in its parent or sibling directories cannot be imported.
That is, unless you add the path to the desired module to PYTHONPATH or add it to sys.path
.
Solution
Add the following code to run.py
, I will explain the code in a bit:
import os, sys
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
from common import a
print("Execution of run starts")
a.CommonFunction()
print("Execution of run finishes")
Run the code again and you will see the expected output:

So what happened?
In the code, we import Python’s built-in modules os
and sys
.
Then we get the current directory using the current file, then get the parent directory using the current directory and just append
it to the sys.path
. What this does is that it adds the parent directory to sys.path. So now we can import any package & its modules into our script, thanks to appending to sys.path
You can go as many levels up as you wish by getting the parent directory recursively.
Alternative code
We can also slightly adjust the above code for programmatically updating sys.path
as follows:
import sys, os, inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)
The change from the earlier code is that we use the inspect
module to get the current file, and then use sys.path.insert
.
Difference between sys.path.insert and sys.path.append
The difference between sys.path.insert
and sys.path.append
is that append
will add the given path to the end of the sys.path
‘s list, while insert
is going to insert it at the given index(0 in our case).
Python searches for the directories to import by going through the list. Now if you have a package or module with the same name, then Python is going to load the module that comes first in the list and will ignore the one that comes later. So if you have name conflicts and defined files/folders with the same name, you must insert that path to sys.path
at the first position.
Otherwise if you dont have naming conflicts, then the ordering of the imports does not matter and you can just append those paths to sys.path
.
Conclusion
Although this solution is a bit hacky and it is recommended to not meddle with sys.path
programmatically, it does get the job done when we have no other choice in Python but to import from parent directory using sys path. Using the os
and sys
module in Python gives us a way to go up a layer or two.
Excellent and useful. Can also add that the same is true for sibling folders.
If move up a branch then need to add:
parentdir = os.path.dirname(parentdir)
LikeLike
God bless. The imports were so frustrating to me
LikeLike