ModuleNotFoundError when importing subpackage using parent package name

Viewed 241

I am trying to model an established Python project based on the recommendeded project structure according to https://docs.python-guide.org/writing/structure/.

After switching to this new structure, I now need to run the script with $ python3 -m sample.runner instead of just $ python3 runner.py or else I get a ModuleNotFoundError. I am trying to understand why that is and whether or not it can be avoided?

Runnning $ python3 runner.py with the new structure gives me:

$ python3 runner.py 
Traceback (most recent call last):
  File "runner.py", line 1, in <module>
    from sample.subpkg1 import subpkg1_file1 
ModuleNotFoundError: No module named 'sample'

Old project structure:

repo_root:
 | 
 |  |
 | ./subpkg1:
 |  | __init__.py
 |  | subpkg1_file1.py
 |  |
 | ./test:
 |  | __init__.py
 |  | bar.py
 |  |
 | runner.py
 | .coveragerc
 | README.md
 | tox.ini
 | 

The new project structure:

repo_root:
 | 
 | ./sample:
 |  | __init__.py
 |  | runner.py
 |  |
 |  | ./subpkg1:
 |  |  | __init__.py
 |  |  | subpkg1_file1.py
 |  |
 | ./test:
 |  | __init__.py
 |  | bar.py
 |  |
 | .coveragerc
 | README.md
 | tox.ini
 | 

I updated an import in runner.py with sample to satisfy tox tests. Without it, running tests with tox fails with a ModuleNotFoundError: No module named 'subpkg1' message. My guess is that is because I am running tox from the repo_root, but I don't know that for sure.

runner.py

from sample.subpkg1 import subpkg1_file1 

def main():
    subpkg1_file1.print_subpkg1()


if __name__ == "__main__":
    main()

subpkg1_file1.py (though this shouldn't matter):

def print_subpkg1():

    print("Printing from subpkg1!")
1 Answers

The sample module is not know because Python does not know where to look for it; it is not in PYTHONPATH. You can extend it at the beginning of the script if needed:

import sys
if 'sample' not in sys.modules:
    from os import path as osp
    sys.path.insert(0, osp.dirname(osp.dirname(__file__)))
del os, sys

from sample.subpkg1 import subpkg1_file1 

def main():
    subpkg1_file1.print_subpkg1()


if __name__ == "__main__":
    main()

```
Related