Here is a link to a project and output that you can use to reproduce the problem I describe below.
I'm using coverage with tox against multiple versions of python. My tox.ini file looks something like this:
[tox]
envlist =
py27
py34
[testenv]
deps =
coverage
commands =
coverage run --source=modules/ -m pytest
coverage report -m
My problem is that coverage will run using only one version of python (in my case, py27), not both py27 and py34. This is a problem whenever I have code execution dependent on the python version, e.g.:
def add(a, b):
import sys
if sys.version.startswith('2.7'):
print('2.7')
if sys.version.startswith('3'):
print('3')
return a + b
Running coverage against the above code will incorrectly report that line 6 ("print('3')") is "Missing" for both py27 and py34. It should only be Missing for py34.
I know why this is happening: coverage is installed on my base OS (which uses python2.7). Thus, when tox is run, it notices that coverage is already installed and inherits coverage from the base OS rather than installing it in the virtualenv it creates.
This is fine and dandy for py27, but causes incorrect results in the coverage report for py34. I have a hacky, temporary work-around: I require a slightly earlier version of coverage (relative to the one installed on my base OS) so that tox will be forced to install a separate copy of coverage in the virtualenv. E.g.
[testenv]
deps =
coverage==4.0.2
pytest==2.9.0
py==1.4.30
I don't like this workaround, but it's the best I've found for now. Any suggestions on a way to force tox to install the current version of coverage in its virtualenv's, even when I already have it installed on my base OS?
I came upon this problem today, but couldn't find an easy answer. So, for future reference, here is the solution that I came up with.
envlist that contains each version of Python that will be tested and a custom env for cov.COVERAGE_FILE environment varible to store the .coverage file in {envdir}.cov env I use two commands.
coverage combine that combines the reports, andcoverage html to generate the report and, if necessary, fail the test..coveragerc file that contains a [paths] section to lists the source= locations.
tox.ini:
[tox]
envlist=py27,py36,py35,py34,py33,cov
[testenv]
deps=
pytest
pytest-cov
pytest-xdist
setenv=
py{27,36,35,34,33}: COVERAGE_FILE={envdir}/.coverage
commands=
py{27,36,35,34,33}: python -m pytest --cov=my_project --cov-report=term-missing --no-cov-on-fail
cov: /usr/bin/env bash -c '{envpython} -m coverage combine {toxworkdir}/py*/.coverage'
cov: coverage html --fail-under=85
.coveragerc:
[paths]
source=
src/
.tox/py*/lib/python*/site-packages/
The most peculiar part of the configuration is the invocation of coverage combine. Here's a breakdown of the command:
tox does not handle Shell expansions {toxworkdir}/py*/.coverage, so we need to invoke a shell (bash -c) to get the necessary expansion.
.coverage file dependency for each pyNN env./usr/bin/env bash -c '...' to ensure we get the correct version of bash. Using the fullpath to env avoids the need for setting whitelist_externals.'{envpython} -m coverage ...' ensures that we invoke the correct python and coverage for the cov env.cov env is dependent on the invocation of py{27,36,35,34,33} which has some not so desirable side effects.
cov through tox.tox -ecov because, either
.coverage file, ortox -epy27,py36,cov), then wipe out the .tox directory first (rm -rf .tox) to avoid the missing .coverage file problem.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With