8. The Python Import System#
Main takeaways:
Suppose we have a directory called
root
with the following files:package/module.py
, andpackage/script.py
.From inside
root
, runningpython3 package/script.py
will addpackage
tosys.path
meaningfrom package import module
will raise an import error inscript.py
.From inside
root
, runningpython3 -m package.script
will addroot
tosys.path
meaningfrom package import module
will not raise an import error inscript.py
When importing a package or module, python searches for it on its
sys.path
. Suppose we have the following project structure.
root
└── package
├── module.py
└── script.py
Where module.py
contains the following code.
def print_module():
print("Print from module.py")
And script.py
has the following code.
from package.directory import module
print_module()
Executing script.py
from root
gives us an import error.
$ python3 package/script.py
Traceback (most recent call last):
File "/home/alex/root/package/script.py", line 1, in <module>
from package.directory import module
ModuleNotFoundError: No module named 'package'
Even though we are running the code from the root
directory which
contains package
, we cannot import module
into
script.py
. Let us print sys.path
at the top of
script.py
and rerun the script.
# script.py
import sys; print(sys.path)
from package import module
print_module()
Running script.py
, we get.
$ python3 package/script.py
['/home/alex/root/package', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/alex/documents/notes/.venv/lib/python3.10/site-packages']
Traceback (most recent call last):
File "/home/alex/root/package/script.py", line 2, in <module>
from package.directory import module
ModuleNotFoundError: No module named 'package'
The first element in the list points to our package
directory. This
means that python will look for package
within the package
directory. We want it to look for package
for the root
directory.
This import error can also be solved by removing package
from the
import path.
# script.py
import module
module.print_module()
The code now runs successfully from root
.
$ python3 package/script.py
Print from module.py
However, if we move script.py
into root
we will get the same
import error again. For clarity, moving script.py
into root
means our file structure now looks like this.
root
├── script.py
└── package
└── module.py
Executing script.py
results in the import error.
$ python3 script.py
Traceback (most recent call last):
File "/home/alex/root/script.py", line 1, in <module>
import module
ModuleNotFoundError: No module named 'module'
To get the script to run successfully, we would have to change our import from
import module
to from package import module
.
For simplicity, I want to be able to import package.module
into a
script no matter where the script is in the root
directory. We will
discuss three ways of doing this but first let us change our project structure
slightly to include script1.py
and script2.py
.
root
├── script1.py
└── package
├── script2.py
└── module.py
Where script1.py
and script2.py
have just about the same code.
# script1.py
from package import module
module.print_module()
# script2.py
from package import module
module.print_module()
The first script, script1.py
, runs successfully but script2.py
does not.
$ python3 script1.py
Print from module.py
$ python3 package/script2.py
python3: can't open file '/home/alex/root/script2.py': [Errno 2] No such file or directory
Now we will run through three ways of solving this issue.
8.1. Running Scripts and Modules#
The -m
flag can be used to run our program as a module instead of a
script. This will make both of script1.py
and script2.py
work.
$ python3 -m script1
Print from module.py
$ python3 -m module.script2
Print from module.py
Let us run this experiment again but print sys.path
at the start of the
scripts.
# script1.py
import sys; print(sys.path)
from package import module
module.print_module()
# script2.py
import sys; print(sys.path)
from package import module
module.print_module()
Running the scripts gives us the following.
$ python3 -m package.script2
['/home/alex/root', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/alex/documents/notes/.venv/lib/python3.10/site-packages']
Print from module.py
$ python3 -m script1
['/home/alex/root', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/alex/documents/notes/.venv/lib/python3.10/site-packages']
Print from module.py
Running the scripts with -m
flag adds the root
directory to
sys.path
. If we cd
into package
and run
script2.py
we get the familiar import error.
$ cd package
$ python3 -m script2
['/home/alex/root/package', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/alex/documents/notes/.venv/lib/python3.10/site-packages']
Traceback (most recent call last):
File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
exec(code, run_globals)
File "/home/alex/root/package/script2.py", line 2, in <module>
from package import module
ModuleNotFoundError: No module named 'package'
So, as long as we run our scripts from root
with the -m
flag,
we should be able to import package.module
since root
has been
added to sys.path
.