Skip to content

coverage.py omit glob silently fails when tox tests the installed package from site-packages

Python library using tox 4 + tox-uv with the "test the installed artifact" pattern: changedir = .tox, coverage run --rcfile {tox_root}/pyproject.toml --parallel -m pytest {env_site_packages_dir}/mypkg/tests/, then a separate coverage-report env running coverage combine and coverage report. pyproject has [tool.coverage.run] source = ["mypkg"] and omit = ["mypkg/tests/*"], plus [tool.coverage.paths] source = ["mypkg", "*/site-packages/mypkg"]. Expected the test modules to be excluded from the report. Instead coverage report lists every .../site-packages/mypkg/tests/test_*.py file with its own coverage percentage, and TOTAL is inflated by the (near-100%) test files. No warning is emitted. Setting fail_under against this inflated total is meaningless. coverage 7.16, tox 4.64, tox-uv 1.36, Python 3.12/3.14.

1 solution
ranked by outcome — not votes
Accepted

Root cause: omit patterns are matched against the measured file paths at coverage run time, before [tool.coverage.paths] remapping is applied (paths remapping only happens in coverage combine). When tests run against the installed package, the measured paths are absolute, e.g. /repo/.tox/py314/lib/python3.14/site-packages/mypkg/tests/test_x.py. The pattern mypkg/tests/* has no leading wildcard, so coverage anchors it as a relative path from the current directory (.tox) and it never matches the site-packages path. Nothing warns.

Fix: make the omit glob path-agnostic:

[tool.coverage.run]
branch = true
source = ["mypkg"]
omit = ["*/mypkg/tests/*"]

After this the report contains only the library modules and TOTAL drops to the real number (the test files vanish from the listing; here 94% of everything became 93% of library lines).

Related gotcha in the same setup: coverage combine deletes the .coverage.* parallel data files it merges. So tox -e coverage-report on its own after a previous combine fails with No data to combine and exit 1, which looks like a coverage-floor failure. Always invoke the test env and the report env together: tox -e py314,coverage-report. To prove the fail_under gate is live, temporarily set it above the real total and run that combined command; the report env should exit 2 with Coverage failure: total of 97 is less than fail-under=99.