Wow, Python can be painful sometimes.
I was setting up a test directory for my project. The repo structure is
├── pyproject.toml
├── requirements.txt
├── src
│ ├── __init__.py
│ ├── presentation.py
│ └── weatherdl.py
├── tests
│ ├── __init__.py
│ └── a_test.py
I tried a few different ways of importing my project source code into the test directory. One way that appeared promising was importing via a context file. Another way was to create a __init__.py
file in the tests directory with code to modify the system path:
import os
import sys
PROJECT_PATH = os.getcwd()
sys.path.append(PROJECT_PATH)
Problem
Both of these methods appeared to work initially, but they had the weird problem that I could only import one of my source files.
The following code worked fine:
import presentation
def a_test():
assert True == True
but as soon as I added another import, the code failed!
import presentation
import weatherdl
def a_test():
assert True == True
ModuleNotFoundError: No module named 'presentation'
Solution
The pytest documentation explains that the default import mode is prepend
not importlib
. I don’t fully understand the difference, but the text contains this little gem.
The
importlib
import mode does not have any of the drawbacks above, becausesys.path
is not changed when importing test modules.
My experience of having imports work when there is one of them, and not otherwise, is probably due to the sys.path
being changed by the imports themselves.
The solution was to change pytest
to use importlib
mode. To do this, I created a pyproject.toml
file in the repository root with the following contents.
[tool.pytest.ini_options]
addopts = [
"--import-mode=importlib",
]
pythonpath = "src"
And that’s it!