13

I have the following project structure:

project/
    example/
        __init__.py
        foo.py
        boo.py
        meh.py
    tests/
        example/
            test_foo.py
            test_boo.py
            test_meh.py

As example, I'm importing foo.py in boo.py as import example.foo as f. And I'm running tests with python3 -m pytest -s -v --cov tests on root folder (project). The unit tests are running very smooth but when I try to run a single file as python3 example/boo.py I got an error:

ModuleNotFoundError: No module named 'example'
  • What is the right approach? –  Mar 11 '19 at 13:25
  • 1
    The right approach is not to run modules directly. – 9769953 Mar 11 '19 at 13:26
  • Note that pytest will also not run the modules, since there is no `test` prepended. Perhaps if you pass the `--docttest-modules` option, but then it will run the doctests, not the actual modules. – 9769953 Mar 11 '19 at 13:27
  • Ok. Im new to python and this could sound strange, but each file from module should not be imported by relative files? –  Mar 11 '19 at 13:28
  • Why should they not be? – 9769953 Mar 11 '19 at 13:28
  • I'm splitting my code into several files to avoid a large single file and keep responsabilities separeted. I don't understand what do you mean by 'The right approach is not to run modules directly'. Could you explain, please? –  Mar 11 '19 at 13:30
  • A module should be imported, not run like a script. That is what a module is for. If, for some reason, you really really feel you need to execute module, then 1/ reconsider, 2/ rewrite your module, 3/ wrap a script around that module by calling the necessary function(s) inside the module (and keep the script itself relatively short). – 9769953 Mar 11 '19 at 13:31
  • Got that! So I should have a script and run the script, not the module. Right? –  Mar 11 '19 at 13:33
  • Yup. __________ – 9769953 Mar 11 '19 at 13:35
  • Dumb question: is possible to have a script inside module folder and run it importing other files from module? –  Mar 11 '19 at 13:36
  • 2
    Put scripts in a separate directory next to your package, e.g. `project/example/` and `project/scripts/`. – 9769953 Mar 11 '19 at 13:41
  • 2
    It may be worth reading up on [Python packaging](https://packaging.python.org/), though admittedly, there is a lot to read and learn. It definitely extends outside the scope of your question, but it would hopefully answer questions such as about where scripts should go. – 9769953 Mar 11 '19 at 13:43

2 Answers2

15

Modules inside a package shouldn't really be run (some exceptions).

But, you can set a PYTHONPATH before running the module if you really want. For a one off, use e.g.

PYTHONPATH=$(pwd) python3 example/boo.py

An alternative is to use relative imports: from . import foo as f inside boo.py. But that still implies that modules shouldn't really be run.


To elaborate a bit more: A module should be imported, not run like a script. That is what a module is for. If, for some reason, you really really feel you need to execute module, then 1/ reconsider, 2/ rewrite your module, 3/ wrap a script around that module by calling the necessary function(s) inside the module (and keep the script itself relatively short).

Note that setuptools already has this functionality through entry points.

A simpler alternative is to use a proper

if __name__ == '__main__':
    main()

line at the end of your module, where main() calls into your module functionality, then execute the module using the Python -m switch:

python -m mypackage.mymodule

But, again, try and limit this functionality.

djvg
  • 11,722
  • 5
  • 72
  • 103
9769953
  • 10,344
  • 3
  • 26
  • 37
7

It's usually a problem with environment variables. You can force the path using the following, and the import should work under all circumstances:

import sys
sys.path.append("/absolute/module/path")
import local_module
James Mc
  • 549
  • 6
  • 10