coverage.renderer

SpaRenderer: emit the covapp SPA coverage report (Plan C).

The pivot renderer — this is what makes the built covapp bundle (Task 2) THE coverage report. It copies the static bundle (index.html + dist/) into the report directory and writes the JS data chunks (Task 1’s spa_data.emit_chunks) that bundle consumes at file:// or any served subpath (spec §2 — no server, no ES modules, no network fetches).

Mirrors the retired Jinja renderer’s missing-dist degrade (_copy_static) without importing from it.

otto.coverage.renderer.spa_renderer.STATIC_DIR = PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/otto-sh/checkouts/stable/src/otto/_webassets/covapp')

The built covapp bundle (make web -> vite build --config vite.covapp.config.ts).

Not committed — a hostless unit-test checkout that skipped make web has no index.html/dist/ here; SpaRenderer._copy_bundle degrades gracefully rather than failing the whole report.

class otto.coverage.renderer.spa_renderer.SpaRenderer(output_dir: Path, project_name: str = 'Coverage Report', *, extra_markers: list[str] | None = None, prefix: Path | None = None)

Bases: object

Render a CoverageStore to a covapp SPA report directory.

Parameters:
  • output_dir – Directory to write the report into (created if needed).

  • project_name – Title shown in the report (IndexPayload.project_name).

  • extra_markers – Extra source exclusion-marker strings (spec §8), forwarded from [coverage.exclusions].markers via the reporter. Scanned alongside the built-in LCOV_EXCL_* markers when annotating each file’s source (see spa_data.emit_chunks).

  • prefix – Strip this leading directory from file paths shown in the report (display only, like genhtml --prefix). Files outside the prefix display unchanged; chunk names always use the full canonical path, never the display path.

render(store: CoverageStore) None

Render the full SPA report: copy the bundle, then emit the data chunks.

Emit the JS data chunks the coverage SPA (Plan C) consumes.

This is the pure-Python half of the SPA report: it turns a CoverageStore into

  • cov_data/index.js — one classic-script assignment (window.__OTTO_COV__ = {...};) carrying the report-wide IndexPayload-shaped dict (config, run table, and a directory tree of rollup Stats), and

  • cov_data/files/<mangled>.js — one classic-script call (window.__OTTO_COV_FILE__({...});) per file, carrying its annotated source and per-line hit/branch/state data.

It deliberately mirrors the mechanics of the retired Jinja renderer (display-path prefix stripping, path mangling, tier labels/colors, the source-exclusion scan) without importing from it.

otto.coverage.renderer.spa_data.OTTO_COV_DATA_FORMAT : int = 1

IndexPayload["format"] / FileChunk["stamp"]-adjacent format marker.

Bump alongside the TypeScript EXPECTED_DATA_FORMAT constant, or never.

otto.coverage.renderer.spa_data.make_stamp() str

Return a fresh report stamp: UTC timestamp + a short random suffix.

Written once per emit_chunks() call and carried verbatim on the index payload and every file chunk, so the frontend can detect a stale/partial chunk cache against a freshly generated index.

otto.coverage.renderer.spa_data.mangle_path(path: Path) str

Mangle a canonical file path into a filesystem/URL-safe chunk id.

Same scheme the retired Jinja renderer used: full canonical path (never the display path), so chunk filenames stay stable across --prefix changes.

otto.coverage.renderer.spa_data.build_index_payload(store: CoverageStore, *, project_name: str, prefix: Path | None, stamp: str) dict[str, Any]

Build the IndexPayload dict written to cov_data/index.js.

Reads whatever exclusion data is already on each FileRecord — it does not scan source itself; emit_chunks() runs that scan (mirroring the retired Jinja renderer) and annotates the store before this function’s per-file stats are computed.

otto.coverage.renderer.spa_data.emit_chunks(store: CoverageStore, output_dir: Path, *, project_name: str, prefix: Path | None, extra_markers: list[str] | None, stamp: str) None

Write cov_data/index.js and one cov_data/files/<mangled>.js per file.

Every file’s source-exclusion scan runs first (annotating FileRecord.excluded_lines on the store), so the index payload’s per-node flags.excluded counts reflect it — the reporter’s later store.save() then persists the same annotation, exactly like the retired Jinja renderer did.