SupercovBlog

Guide

pytest coverage exclude: omit files, folders and lines

The omit patterns and pragmas that work with pytest-cov 7.1 and coverage.py 7.17, and six that look right but are not.

exclude_lines replaces. exclude_also adds. A brushed-aluminum plate of white code lines, with one violet line lifted out of it.

Put file patterns in omit under [tool.coverage.run] and start them with a wildcard: */generated/*. To leave out lines, add # pragma: no cover, and add your own patterns with exclude_also, never exclude_lines.

Exclude patterns at a glance

To excludeWrite
A folder anywhereomit = ["*/generated/*"]
A folder from the project rootomit = ["src/shop/generated/*"]
A file by name, in any folderomit = ["money.py"]
From the command linecoverage run --omit='*/generated/*' -m pytest
One line or block# pragma: no cover on the line that opens it
Every line like a patternexclude_also = ["if __name__ == .__main__.:"]

pytest-cov has no --omit flag. It reads the same configuration as coverage.py: pyproject.toml, .coveragerc, setup.cfg or tox.ini.

We ran every setup here with pytest 9.2.0, pytest-cov 7.1.0 and coverage.py 7.17.2 on Python 3.13. The project has a shop package under src, with a generated client and a legacy module no test imports. With --cov=shop and no exclusions, the report has 17 of 21 statements (81%).

Omit files and folders

[tool.coverage.run]
omit = [
    "*/generated/*",
    "*/legacy/*",
]
pytest --cov=shop

The report then had 14 of 15 statements (93%). src/shop/generated/* did the same. A pattern that starts with a wildcard is used as is; any other pattern is read from the folder you run pytest in.

Use --cov=shop, not a bare --cov. A bare --cov measured our test file too, and left out the legacy module no test imports. --cov=shop reported that module, at 0 of 2 statements, and no test files.

Exclude lines with pragma: no cover

def fmt(cents: int) -> str:
    if cents < 0:  # pragma: no cover
        return "-" + fmt(-cents)
    return f"{cents // 100}.{cents % 100:02d}"

The comment on the if excludes the whole branch under it. Coverage.py 7.17 also excludes if TYPE_CHECKING: blocks and ... stub bodies on its own.

To exclude every line of a kind, add a regular expression with exclude_also:

[tool.coverage.report]
exclude_also = [
    "if __name__ == .__main__.:",
]
Two columns. exclude_also keeps coverage.py's defaults, pragma: no cover, if TYPE_CHECKING and stub bodies, and adds your pattern: receipt.py has 5 of 5 statements. exclude_lines replaces the defaults with your pattern: the pragma is ignored and receipt.py has 7 of 9.
The same pattern set two ways. exclude_lines drops coverage.py's defaults, pragma: no cover included.

Why your pytest coverage exclude isn’t working

What we triedWhat happened
omit = ["generated/*"]Nothing omitted: read from the run folder, not the package
omit = ["src/shop/generated"] (no /*)Nothing omitted
omit = ["shop.generated.*"] (a module name)Nothing omitted
exclude_lines instead of exclude_also# pragma: no cover and if TYPE_CHECKING: stopped working: 7 of 9 statements instead of 5 of 5
omit in pyproject.toml and a .coveragerc in the same folder.coveragerc won; the pyproject.toml setting was ignored
A bare --covTest files in the report, unimported modules missing
A grid of six setups against five files. --cov=shop without omit keeps pricing, receipt, money and legacy, 17 of 21 statements. omit */generated/* or src/shop/generated/* leaves pricing and receipt, 14 of 15. omit generated/* or shop.generated.* removes nothing. A bare --cov adds the test file and drops legacy, 29 of 31.
What each setup left in the report, measured with pytest-cov 7.1.0 and coverage.py 7.17.2.

exclude_lines replaces coverage.py’s default patterns, including the one for # pragma: no cover. exclude_also adds to them. If you see exclude_lines in an old answer, switch it.

omit also works under [tool.coverage.report]. There it hides files from the report but still measures them, which looks the same in the report.

What Supercov adds

npx supercov -- pytest
npx supercov runs latest gaps

Supercov runs the same pytest command and records which test covered each line, with branches and MC/DC. It decides what counts from your source directories, not from omit. See pytest-cov vs coverage.py for the two ways to run coverage, and Python code coverage for the tools side by side.