0

I've got a test script which imports modules from my application. This works when run with python -munittest, but it fails with ModuleNotFoundError if I simply use python tests/test_app.py.

This also happens with other scripts in this project which aren't unit tests.

$ python tests/test_app.py
Traceback (most recent call last):
  File "tests/test_app.py", line 2, in <module>
    import myapp
ModuleNotFoundError: No module named 'myapp'

$ python -munittest tests.test_app
....
----------------------------------------------------------------------
Ran 4 tests in 0.119s

OK

As you can see from the trace, it fails at the very beginning of the file, in the imports, where I use import myapp.

Project structure:

/project
  /myapp
    __init__.py
    models.py
  /otherapp
  /anotherapp
  /tests
    test_app.py
  /scripts
    a_script.py

What magic is the unittest module doing to make my script load?

How do I execute my script directly?

user3427070
  • 499
  • 5
  • 14

1 Answers1

0

It fails because when you run with python tests/test_app.pyyour python will look for myappin the local folder relative to the test_app.py or in PYTHONPATH and won't find it. Whereas unittest will handle importing the package while starting from project root folder.

This python documentation link explain how import statement work

When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:

  • the directory containing the input script (or the current directory).
  • PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
  • the installation-dependent default.

After initialization, Python programs can modify sys.path. The directory containing the script being run is placed at the beginning of the search path, ahead of the standard library path. This means that scripts in that directory will be loaded instead of modules of the same name in the library directory. This is an error unless the replacement is intended. See section Standard Modules for more information.

Therefore, to make your script directly executable you need to tell python to find "myapp" using one of the ways described in the documentation. Here's another SO question discussing how to use import (you can find a lot more with a quick search).

Mohamed Ali JAMAOUI
  • 14,275
  • 14
  • 73
  • 117
  • It sounds like the proper solution (without writing code I'll throw away as the project grows into a larger application) is to bite the bullet and start using virtualenv now? Or, is there an easier way that is still good practice that would allow someone to clone my project and simply `cd project; python scripts/a_script.py`? – user3427070 Jul 07 '17 at 08:55
  • To use `PYTHONPATH`, it seems I'd have to ask the users to first add it to their environment variables, or commit a shell script wrapper to run python scripts. – user3427070 Jul 07 '17 at 08:58
  • For each folder (apart from the root and tests) of your project structure you need to add __init__.py then the user will be able to do imports after installation of the package. Other than that, if you want your package to be used as any python package you need to add setup.py (check this link for more: https://stackoverflow.com/questions/1471994/what-is-setup-py) – Mohamed Ali JAMAOUI Jul 07 '17 at 09:05
  • Thanks! I went into every folder and added `__init_.py` with the standard boilerplate. This alone didn't solve the problem, but I was then able to add `import __init__` to the top of each script and `sys.path.insert(0, os.getcwd())` which fixes it. – user3427070 Jul 07 '17 at 17:14