Reputation: 9572
I have a Python project that requires some compilation. tox
makes the sdist
and the bdist_wheel
successfully. But when tox
runs the tests, the source code module is always the one that get imported, causing the tests to fail (because there are no binaries there). This happens because the root of the project is always the first folder on sys.path
and I cannot figure out how this happens.
project
├── foo
│ ├── __init__.py
│ └── bar.c <-- turns into bar.so in bdist_wheel
├── tests
│ ├── __init__.py
│ └── test_bar.py
├── tox.ini
└── setup.py
tox.ini
[tox]
envlist =
py37
[testenv]
changedir = {toxinidir}/tests
setenv =
PYTHONPATH = {envsitepackagesdir}
commands = {envpython} -m pytest --cov=bar --cov-report=html
Note that I set changedir
to ensure that the project root is not the working directory. I also set PYTHONPATH
to only the tox environment's site-packages. I also force the tox environment's Python to be run.
Nevertheless, this is sys.path
I see if I print it out during test running:
[
'/home/me/foo', # Bad, bad, bad
'/home/me/foo/tests', # Good, this is the CWD
'/home/me/foo/.tox/py37/lib/python3.7/site-packages', # Good
'/home/me/foo/.tox/py37/lib/python37.zip', # Umm, ok, this doesn't exist
'/home/me/foo/.tox/py37/lib/python3.7', # Good
'/home/me/foo/.tox/py37/lib/python3.7/lib-dynload', # Probably good
'/home/me/anaconda/envs/foo/lib/python3.7', # Bad, why is this here?
]
The biggest offender is /home/me/foo
which causes the source module foo
to be loaded instead of the one installed in tox
.
Another problematic one is /home/me/anaconda/envs/foo/lib/python3.7
. I would prefer that tox
not fall back on the system interpreter if something is not found in the environment.
How does tox choose this these paths and how do I control it to be better behaved?
Upvotes: 5
Views: 3406
Reputation: 9572
This is not caused by tox; it is caused by pytest. As described in the pytest documentation, there is known to be a bad interaction between pytest and tools like tox that install the distribution in an isolated environment.
The reason for this is that pytest modifies sys.path
so that all folders containing test packages (test folders with __init__.py
in them) are added to it. Because the foo/tests
directory contains a __init__.py
file, pytest adds foo/
to sys.path
so that the tests
package can be imported with the normal import
statement.
Even removing __init__.py
may not be enough. Running pytest as python -m pytest
in the foo
directory will cause the foo
directory to be added to sys.path
. Running pytest with the pytest
command line tool will avoid this.
The recommended solution is to move all source to a dedicated src
directory. This ensures that the source packages will never be on sys.path
even if the project root is.
project
├── src
│ └── foo
│ ├── __init__.py
│ └── bar.c
├── tests
│ ├── __init__.py
│ └── test_bar.py
├── tox.ini
└── setup.py
Upvotes: 5