Understanding Python's Import Context

hai nguyenhai nguyen
2 min read

Importing modules in Python can be confusing. It works, but many people don't understand why it works.

A typical scenario: We encounter a ModuleNotFoundError, search online, and eventually we find a workaround or a way to make it work, but we don't understand why it works! pytest documentation wisely emphasizes this aspect.

The root cause of the confusion

The root cause of the confusion is how you run a Python file, which affects the Python module search path. In other words, it affects sys.path, the directory Python uses to search for modules.

Let's look at the example below, with the files main.py and sub/b.py:

project
└───sub
│   │   __init__.py
│   │   a.py
│   │   b.py
│   └───subsub
│       │   c.py
└───main.py
# main.py
from sub.b import func_b

if __name__ == "__main__":
    import sys
    print(sys.path)
    print("Hello World!")
    func_b()
# module sub/b.py
from a import func_a1 # This should Fail if you run python main.py

def func_b():
    print("call func_b() in b.py")
    func_a1()
    import sys
    print(sys.path)

if __name__ == "__main__":
    func_b()

If you now run python main.py, guess what? It will fail at this line in the file sub/b.py.

from a import func_a1
(pythopn-snippet-py3.10) (base) hai@hai-virtual-machine:~/Dev/pythopn-snippet/import_play$ python main.py 
Traceback (most recent call last):
  File "/home/hai/Dev/pythopn-snippet/import_play/main.py", line 1, in <module>
    from sub.subsub.c import func_c
  File "/home/hai/Dev/pythopn-snippet/import_play/sub/subsub/c.py", line 2, in <module>
    from ..b import func_b  # also work
  File "/home/hai/Dev/pythopn-snippet/import_play/sub/b.py", line 2, in <module>
    from a import func_a1 # This should Fail if you run python main.py
ModuleNotFoundError: No module named 'a'

Hmm, why? Module "a" is in the same folder as module "b." Why does Python say "No module named 'a'?

This is because sys.path doesn't include the sub/ folder in its search path. The module named "a" only makes sense in the sub/ folder; it doesn't make sense elsewhere.

When you run python main.py, sys.path will include the current folder of main.py, which is the project folder. In the project folder, there is no module a.py, so it will fail.

Let's make a small change to b.py.

from sub.a import func_a1
from .a import func_a1

Now, running python main.py should be successful.

Explanation: sys.path includes the project folder, which contains the sub package.

So, it will be able to find sub.a! The second import line uses a relative import. Since a.py and b.py are in the same directory, it will work.

Sample code used in this article: python-note/import_play at main · nguyenhaiquoc/python-note (github.com)

0
Subscribe to my newsletter

Read articles from hai nguyen directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

hai nguyen
hai nguyen