diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-31 12:31:35 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-31 12:31:35 -0500 |
| commit | af478a42b18c4d5e0712c4cb43036126d36c56b5 (patch) | |
| tree | 5ef37e4f4c0e1f749e1d3506dde4147036660854 /todo.org | |
| parent | ddf48dc7ac780da1aacdff4e03f1d7da255b8f39 (diff) | |
| download | rulesets-af478a42b18c4d5e0712c4cb43036126d36c56b5.tar.gz rulesets-af478a42b18c4d5e0712c4cb43036126d36c56b5.zip | |
feat(python): add coverage-summary to the Python bundle
Second language in the coverage-summary fan-out, after the Elisp pilot. Same kernel: a module no test imports never appears in coverage.py's report, so a line-weighted total skips it silently and the suite looks healthier than it is. This counts every source file on disk that's absent from the report as 0% and weights the project number by file, so untested modules stay visible.
The script at languages/python/claude/scripts/coverage-summary.py parses coverage.py's JSON (files[path].summary.covered_lines / num_statements), resolves report paths against the report's directory since coverage records them relative to where it ran, and recurses the source dir for *.py. Unlike the Elisp version it doesn't print a per-file table, because coverage.py's own coverage report already does. The script adds the missing-file accounting that report lacks. It uses only the standard library, parsing the report rather than importing coverage.
The Python run confirmed the plumbing from the pilot is genuinely generic. install-lang and sync deliver the script and the project-owned coverage-makefile.txt with no Python-specific code. The one gap I had to close: the Python bundle shipped without a gitignore-add.txt, so the .claude/ footprint wasn't ignored and the script would have been committable. Added one mirroring the Elisp footprint plus Python artifacts (__pycache__, .coverage, coverage.json). make test gained a languages/*/tests/test_*.py discovery path alongside the existing Elisp ERT one.
Tests: 12 pytest covering the parser, the file-weighted number, and the missing-file detection including subpackage recursion, plus an install-lang check that the script lands in the gitignored footprint. I proved it against a report matching coverage.py's documented schema and the CLI end to end, but not against a live coverage json run, because coverage.py isn't installed in this repo's env. The first project to adopt it should sanity-check against a real report.
Diffstat (limited to 'todo.org')
| -rw-r--r-- | todo.org | 13 |
1 files changed, 7 insertions, 6 deletions
@@ -1156,18 +1156,19 @@ Reference (dotemacs): =scripts/coverage-summary.el=, =modules/coverage-core.el=, Origin: handoff from the .emacs.d session, 2026-05-25. -** TODO [#C] Fan out coverage-summary to Python, Go, and TypeScript bundles :feature: +** TODO [#C] Fan out coverage-summary to Go and TypeScript bundles :feature: :PROPERTIES: :CREATED: [2026-05-31 Sun] :END: -The Elisp pilot proved the pattern (see the DONE task above). Each remaining bundle needs its own ~40-line parser over that tool's report format, plus a =coverage-makefile.txt= fragment and the prereq harness where one is missing. The bundle plumbing is already generic: =sync-language-bundle.sh= auto-fixes any =claude/scripts/*= and inbox-drops any =coverage-makefile.txt=; =install-lang.sh= seeds the fragment; =make test= discovers =languages/*/tests/test-*.el=. So each language is just: the parser script, its tests, and the fragment. +The Elisp pilot proved the pattern; Python followed (both DONE above). Python confirmed the plumbing is genuinely generic — =sync-language-bundle.sh= auto-fixes any =claude/scripts/*= and inbox-drops any =coverage-makefile.txt=; =install-lang.sh= seeds the fragment; =make test= now discovers both =languages/*/tests/test-*.el= (ERT) and =languages/*/tests/test_*.py= (pytest). So Go and TS are each just: the parser script, its tests, and the fragment. -- Python: =coverage json= per-file JSON, or lean on =coverage report=. Missing-file detection over the package's =*.py= on disk. -- Go: =go test -coverprofile=cover.out=; parse =cover.out= (simple text), or =go tool cover -func=. -- TypeScript/JS: nyc/Istanbul =coverage-final.json= / json-summary. +- Go: =go test -coverprofile=cover.out=; parse =cover.out= (simple text), or =go tool cover -func=. Note Go has no =make test= discovery path yet — add a =go test= runner for =languages/go/tests= when this lands. +- TypeScript/JS: nyc/Istanbul =coverage-final.json= / json-summary. Needs a JS test-discovery path in =make test= too. + +Keep the kernel identical: file-weighted project number, source files absent from the report counted as 0%. Don't reimplement the per-file table where the built-in reporter already prints one — Go's =go tool cover -func= and nyc both do, so those scripts focus on the missing-file list and the project number. -Keep the kernel identical: file-weighted project number, source files absent from the report counted as 0%. Don't reimplement the per-file table where the built-in reporter already prints one — Python and JS both do, so those scripts can focus on the missing-file list and the project number. +Python notes for the next person: the script parses coverage.py's =files[path].summary.{covered_lines,num_statements}= (stable since coverage 5.x), resolves report paths against the report's parent dir (= project root), recurses the source dir for =*.py=, and was proven against a synthetic report matching the documented schema — not yet against a live =coverage json= run (coverage.py wasn't installed in the rulesets env). First real adopter should sanity-check against an actual report. ** TODO [#B] Cross-project pattern catalog :spec:thinking: :PROPERTIES: |
