Share a mypy HTML report
Adopting typing in an existing Python codebase is a long grind with one recurring question: what do we annotate next? The answer is in a report almost nobody generates, because the report has nowhere to go.
pip install 'mypy[reports]'
mypy --html-report mypyreport/ src/
That writes a ranked table of every module by imprecision — the share of
lines mypy could not check precisely — and a page per module with the offending
lines highlighted. It is the closest thing to a prioritised worklist the type
migration will ever have, and it lands in a directory that gets .gitignored.
Precision, not annotation count
The number is worth understanding before anyone argues about it. A module can
be fully annotated and still score badly, because a dependency returns Any
and that Any spreads through everything it touches. That is the useful
signal: it points at the one stub package worth installing rather than at the
hundred functions worth annotating.
Which is also why this should be a link and not a percentage in a CI log. The percentage starts an argument. The page ends it.
Publish the folder
Drag mypyreport/ (or a zip of it) into
Comma:
index.htmlbecomes the report body — the ranked table.- The per-module pages and stylesheet upload alongside it, with relative references rewritten to the uploaded copies, so clicking a module row still opens its annotated source.
- Scripts run inside a sandboxed iframe (
allow-scripts, noallow-same-origin).
From CI
mypy --html-report mypyreport/ src/ || true # report even when the gate fails
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html mypyreport/index.html \
--arg t "Type coverage — $(git rev-parse --short HEAD)" \
'{title: $t, html: $html}')"
The || true matters. A type-coverage report is most interesting on the run
that failed, and a job that exits before publishing throws away the artifact
that explains why. See publishing from CI and the
API reference.
Supporting pages go up through POST /api/v1/reports/$REPORT_ID/assets as
base64.
A revision per run is the migration record
PATCHing one saved report id keeps a permanent URL, and every publish appends a revision. Six months of those is the only honest answer to "is the typing effort actually working?" — better than a graph, because you can open any two points and see which modules moved.
Comments, so a decision stays decided
Half the red rows on a type-coverage report are red on purpose: a legacy module nobody will annotate, a dynamic dispatch layer that resists typing, a dependency without stubs. Anchoring that reasoning to the row means the next engineer reads it instead of rediscovering it. See commenting on HTML.
Limits
- Entry HTML: 5 MB. Assets: 25 MB per file, 250 MB and 500 files total. A large codebase generates one page per module — over 500, publish the index alone, or report per package.
- Scripts run, sandboxed — no same-origin access.
- 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.