Coverage
Karva measures line coverage natively. There is no plugin to install, no .coveragerc, and no separate coverage binary on the path — coverage is part of karva test.
The implementation runs in the test worker on top of sys.monitoring (Python 3.12+) or sys.settrace (older versions), records every executed line under the configured source roots, and prints a Name / Stmts / Miss / Cover table at the end of the run.
Quick start
Pass --cov to measure the current working directory:
| Bash | |
|---|---|
1 | |
| Text Only | |
|---|---|
1 2 3 4 5 | |
Pass a path or importable module or package name to limit measurement to specific source roots. Pass --cov multiple times to measure several:
| Bash | |
|---|---|
1 2 3 | |
Equivalent configuration:
| TOML | |
|---|---|
1 2 | |
An empty entry ("") measures the cwd, matching pytest-cov's bare --cov.
Branch coverage
Pass --cov-branch to measure branch destinations as well as lines:
| Bash | |
|---|---|
1 | |
| Text Only | |
|---|---|
1 2 3 4 5 | |
Branch coverage records line-to-line arcs for conditional control flow and compares them with statically possible branch destinations. The Cover percentage includes both statement and branch opportunities, matching coverage.py's branch coverage model. JSON, XML, and HTML reports include branch data when branch mode is enabled.
Equivalent configuration:
| TOML | |
|---|---|
1 2 3 | |
Reports
--cov-report=term (the default) prints the compact table above. --cov-report=term-missing adds a Missing column listing the uncovered line numbers per file:
| Bash | |
|---|---|
1 | |
| Text Only | |
|---|---|
1 2 3 4 5 | |
--cov-report=xml[:PATH] writes Cobertura XML for CI integrations. If PATH is omitted, karva writes coverage.xml in the project root:
| Bash | |
|---|---|
1 2 | |
Persisted native coverage data can produce the same report after the test run:
| Bash | |
|---|---|
1 2 | |
Cobertura structure, line and branch totals, project-relative class filenames, and XML escaping are supported external contracts for CI consumers.
Equivalent configuration:
| TOML | |
|---|---|
1 2 3 4 | |
--cov-report=json[:PATH] writes a machine-readable JSON report. If PATH is omitted, karva writes coverage.json in the project root:
| Bash | |
|---|---|
1 2 | |
Pass --cov-context=test to record qualified test names and lifecycle phases. Execution outside a test uses session; test execution uses setup, run, or teardown:
| Bash | |
|---|---|
1 | |
Add a static run context in configuration when reports must distinguish environments or shards:
| TOML | |
|---|---|
1 2 | |
Contexts compose as <static>|<qualified-test>|<phase>, for example python=3.14|test_checkout::test_card(visa)|run. Karva escapes \ and | within each component as \\ and \| so names remain unambiguous.
Filter every report to one or more context regular expressions. Any matching context includes its observation, and totals are recalculated from those observations:
| Bash | |
|---|---|
1 2 3 | |
Persisted native coverage data can be exported independently. Output is compact by default:
| Bash | |
|---|---|
1 2 3 | |
Exported JSON is separate from Karva's native artifact. meta.format versions its documented schema; breaking field or semantic changes increment that number, while consumers must tolerate additive fields within a format version. Files contain executed, missing, and excluded lines, optional contexts, branch arcs when collected, and per-file summaries. totals contains aggregate line and branch metrics.
uv run karva coverage lcov writes a deterministic LCOV tracefile from persisted native data. Use --output to select another destination:
| Bash | |
|---|---|
1 2 | |
LCOV SF, DA, LF, LH, BRDA, BRF, and BRH records are a supported external contract. Source paths are portable project-relative paths after configured path aliases are applied.
CI integrations
Prefer Cobertura XML unless an integration specifically expects LCOV. Both exports use repository-relative source paths; run upload and analysis tools from the repository root.
| Integration | Karva export | Configuration |
|---|---|---|
| Codecov | Cobertura XML | Upload coverage.xml; Codecov also accepts LCOV. |
| Coveralls | LCOV | Run coveralls report coverage.lcov --format=lcov. |
| GitLab coverage visualization | Cobertura XML | Upload coverage.xml as a cobertura coverage report artifact. |
| SonarQube Python coverage | Cobertura XML | Set sonar.python.coverage.reportPaths=coverage.xml. |
| diff-cover | Cobertura XML | Run diff-cover coverage.xml from repository root. |
Cobertura <source> is . and each <class filename> is repository-relative. LCOV SF paths are also repository-relative. This keeps reports portable when CI generates and consumes artifacts in different checkout directories.
Combine native artifacts from CI shards before reporting:
| Bash | |
|---|---|
1 2 | |
With no paths, combine discovers artifacts under .karva/coverage/pending/. Successfully consumed inputs are removed after the combined artifact is atomically replaced. Pass --keep to retain them or --append to include the existing combined artifact.
Delete combined native data and recognized pending shards without touching generated reports:
| Bash | |
|---|---|
1 | |
--cov-report=html[:DIR] writes a simple browsable HTML report. If DIR is omitted, karva writes htmlcov/ in the project root:
| Bash | |
|---|---|
1 2 | |
Files that were never imported during the run still appear, at 0%, so dead modules under your source root show up rather than silently inflating the total.
Filtering report files
Use --cov-include and --cov-omit to keep generated files, migrations, vendored code, or other non-target paths out of the report:
| Bash | |
|---|---|
1 | |
Globs match the project-relative file path shown in the coverage report. When include filters are set, only matching files are reported. Omit filters are applied after include filters.
Equivalent configuration:
| TOML | |
|---|---|
1 2 3 4 | |
Failing on low coverage
--cov-fail-under=N exits non-zero when total coverage drops below N, even if every test passed:
| Bash | |
|---|---|
1 | |
N accepts any value in 0..=100, fractional values included. The flag has no effect when tests already failed — the exit code is already non-zero in that case.
| TOML | |
|---|---|
1 2 | |
Disabling for a single run
--no-cov overrides any --cov flag and any [coverage] sources configured in karva.toml:
| Bash | |
|---|---|
1 | |
Use it when iterating locally without editing config — for example, to skip the tracer overhead on a tight feedback loop while CI keeps coverage on.
Excluding code
Append # pragma: no cover to a line to exclude it from the executable-line set:
| Python | |
|---|---|
1 2 3 4 | |
The pragma applies to the line it appears on. When placed on the head of a compound statement (def, class, if, elif, else, except, match, case, with, for, while, try), the entire body of that branch is excluded:
| Python | |
|---|---|
1 2 3 | |
The match is case-insensitive (# PRAGMA: NO COVER works) and is only recognised inside an actual comment — the literal text inside a string is not a directive.
Karva also excludes ellipsis-only placeholder bodies and if TYPE_CHECKING: or if typing.TYPE_CHECKING: clauses by default. These lines do not count as executable or missing.
Use # pragma: no branch when a conditional is executable but one destination is intentionally unreachable:
| Python | |
|---|---|
1 2 | |
while True and literal boolean if conditions are recognised as structurally partial without a pragma. Configured regular expressions can mark project-specific branch lines the same way:
| TOML | |
|---|---|
1 2 | |
Partial-branch rules suppress missing branch destinations only. The source line remains executable and measured.
Source roots
Karva first treats each --cov value as a path relative to the project root. If that path does not exist, Karva resolves it as a module, regular package, or namespace package using the selected Python environment. Existing paths therefore take precedence over importable names. Modules without Python source and unresolved names produce an error naming both attempted interpretations.
Files under explicitly selected importable packages are measured even when they live in site-packages or a virtual environment. Broad path sources still skip nested site-packages, dist-packages, .venv, and .tox directories.
Parallel runs
Each worker writes its own JSON file. After the run, the main process unions the per-file line sets and produces a single report. No coordination flag is required; coverage works the same with --no-parallel or with -n 16.
CI integration
A typical CI invocation pins a minimum and prints the missing lines:
| Bash | |
|---|---|
1 | |
For XML-consuming tools such as SonarQube or Codecov:
| Bash | |
|---|---|
1 | |
For machine-readable JSON or a browsable HTML summary:
| Bash | |
|---|---|
1 2 | |
Or, equivalently, in pyproject.toml:
| TOML | |
|---|---|
1 2 3 4 | |
| Bash | |
|---|---|
1 | |