Live code coverage for a running Flask application.
Project description
flask-coverage
Live code coverage for a running Flask application.
flask-coverage wraps coverage.py as a Flask extension and exposes a small debug blueprint at /debug/coverage. You can introspect what's been executed so far, take snapshots, view a per-file HTML report, and export the raw .coverage data — all from a running process, without restarting it.
It is designed for two scenarios:
- Browser tests. Run your full Flask app under Playwright/Selenium/Cypress, drive it however you like, then read the live coverage report to see which paths your end-to-end tests actually reach.
- Production / canary. Measure what code your live traffic exercises. Coverage measurement carries some overhead (typically <15% on Python 3.12+ with
sys.monitoring), but for low-to-mid QPS services that's a reasonable trade for ground-truth dead-code detection.
Install
pip install flask-coverage
Requires Python ≥ 3.12, Flask ≥ 2.3, coverage ≥ 7.4.
Quickstart
from flask import Flask
from flask_coverage import FlaskCoverage
app = Flask(__name__)
FlaskCoverage(app) # mounts /debug/coverage
Run your app and visit http://127.0.0.1:5000/debug/coverage/.
A runnable demo with a step-by-step walkthrough lives in examples/.
Endpoints
Mounted under /debug/coverage by default (override with FlaskCoverage(app, url_prefix="…")).
| Method | Path | Purpose |
|---|---|---|
GET |
/ |
Dashboard with text report and links to the others |
GET |
/report |
Text report (same format as coverage report) |
GET |
/html/ |
Native coverage.py HTML report (per-file source + line highlighting) |
GET |
/files |
JSON list of measured files: {file, statements, missing, covered, percent} |
POST |
/snapshot |
Flush in-memory counters to disk; returns JSON stats |
GET |
/export |
Download the merged .coverage data file |
POST |
/reset |
Erase all collected data |
Configuration
Coverage settings are read from [tool.coverage.*] in pyproject.toml automatically (via coverage.py's native config support), or from .coveragerc / setup.cfg / tox.ini if present.
[tool.coverage.run]
source = ["myapp"]
parallel = true # recommended for gunicorn/uwsgi (see Multi-worker)
[tool.coverage.report]
omit = ["*/migrations/*", "*/tests/*"]
Starting coverage early
For accurate measurement of module-level code, coverage must start before your application modules are imported. Three options, in order of preference:
-
COVERAGE_PROCESS_STARTenv var (best for gunicorn/uwsgi):export COVERAGE_PROCESS_START=$(pwd)/pyproject.toml gunicorn myapp:app
-
flask-coverageCLI shim:flask-coverage --app myapp run --debug
-
Manual
start_early()as the very first line inwsgi.py:from flask_coverage import start_early start_early() # before any of your app modules from myapp import create_app app = create_app()
If a Coverage instance is already running (any of the above, or pytest-cov in tests), FlaskCoverage(app) adopts it instead of creating a duplicate tracer.
Security
The /debug/coverage blueprint exposes filesystem paths for every measured source file — treat it as sensitive. Registration is fail-closed: it requires one of the following, or it raises RuntimeError:
-
app.debugisTrue, or -
FLASK_COVERAGE_PASSWORDenv var is set (HTTP Basic auth — useradmin, override withFLASK_COVERAGE_USERNAME), or -
a custom
auth=callback is passed toFlaskCoverage(...):FlaskCoverage(app, auth=lambda: current_user.is_authenticated and current_user.is_admin)
The basic-auth check uses hmac.compare_digest for constant-time comparison.
Operations
Disabling without redeploy
Set FLASK_COVERAGE_DISABLED to a truthy value (1, true, yes, on) before the process starts, and FlaskCoverage(app) becomes a no-op: no tracer, no blueprint, no auth check.
FLASK_COVERAGE_DISABLED=1 gunicorn myapp:app
Multi-worker (gunicorn / uwsgi)
Each worker traces independently. Set parallel = true under [tool.coverage.run] so each worker writes .coverage.<host>.<pid>.<rand>. The /report, /files, /html/, and /export endpoints automatically merge sibling files into a temporary copy before generating output, so a request served by any worker sees coverage from all of them.
The merge is non-destructive: original per-worker files on disk are never deleted, so workers keep accumulating data normally.
Performance
On Python 3.12+, coverage.py uses sys.monitoring (PEP 669), which is significantly faster than the legacy sys.settrace path — typically under 15% overhead. Acceptable for staging and canary; profile before turning on for high-QPS production traffic.
Development
git clone https://github.com/abilian/flask-coverage
cd flask-coverage
uv sync
uv run pytest # 45 tests
make check # lint + format + type check
Tests are organised by the test pyramid:
tests/a_unit/— fast, isolated, mock-basedtests/b_integration/— Flask test-client + mocked Coveragetests/c_e2e/— realcoverage.Coveragerunning, including multi-worker simulation
CI runs ruff (lint + format), ty (type check), and pytest across Python 3.12 / 3.13 / 3.14.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flask_coverage-0.1.0.tar.gz.
File metadata
- Download URL: flask_coverage-0.1.0.tar.gz
- Upload date:
- Size: 9.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f62207ebfd4f6cde34943182f631adb1aae5fb72a04fcbe3817ed8b771b7e9a
|
|
| MD5 |
dd7725e9e3dede61e0f2c8ff6fec5d16
|
|
| BLAKE2b-256 |
4c9736636e0afa808dd12cc26be03bc59b735d4862e0f85e9289d1ee3a5bc725
|
File details
Details for the file flask_coverage-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flask_coverage-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83d5669f1ce4ecb1eccc427b1c7dcf84b52329138b50731d1f99b2acc87b8d63
|
|
| MD5 |
831d49c464c7fba895d78b97aa024bc2
|
|
| BLAKE2b-256 |
91e1046ecd5fc86d69c4eb8a99d74df47c432dc319663d782dd5135e10f7d031
|