Skip to content

Ship py.typed marker and enforce pyright baseline gate - #179

Merged
tmgbedu merged 1 commit into
mainfrom
task/py-typed-static-gate-1053
Jul 16, 2026
Merged

Ship py.typed marker and enforce pyright baseline gate#179
tmgbedu merged 1 commit into
mainfrom
task/py-typed-static-gate-1053

Conversation

@tmgbedu

@tmgbedu tmgbedu commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes reportMissingTypeStubs for consumers importing fastapi_startkit (e.g. fastapi_startkit.fastapi) and turns the advisory pyright CI job into an enforced, regression-proof gate.

Changes

  • Add src/fastapi_startkit/py.typed — PEP 561 marker so type checkers trust the package's inline hints. Verified present in the built wheel (fastapi_startkit/py.typed).
  • Add scripts/check_types.py — a baseline gate. Runs pyright and compares the error count to scripts/pyright_baseline.txt:
    • errors > baseline → fail (a regression was introduced)
    • errors < baseline → pass, print a hint to lower the baseline (ratchet)
    • errors == baseline → pass
  • Add scripts/pyright_baseline.txt — current baseline 659 (basic mode, unchanged).
  • lint.yml — pyright job is now blocking and runs the gate instead of continue-on-error.

Why a baseline gate

The package currently has 659 pyright errors in basic mode, so flipping to fully-blocking would break CI. The gate grandfathers existing debt while preventing any new type errors, and ratchets down as errors are fixed.

Testing

  • uv build --wheel → confirmed fastapi_startkit/py.typed inside the wheel.
  • Gate at baseline (659) → pass.
  • Simulated regression (baseline 658) → fail (exit 1).
  • Simulated improvement (baseline 700) → pass with ratchet hint.

Notes: basic mode retained per decision; no source type fixes in this PR (baseline only).

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@tmgbedu tmgbedu left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Code Reviewer verdict: APPROVE — GitHub blocked formal approval since the reviewing account is also the PR author; posting as a comment instead.]

Reviewed the diff, read scripts/check_types.py in full, confirmed CI wiring in .github/workflows/lint.yml, and ran the gate locally against the PR branch (matches pyright's own --outputjson errorCount of 659).

py.typed vs check_types.py — not redundant, different problems:

  • py.typed is a pure PEP 561 packaging marker. It only affects downstream consumers of the published package (their type checkers stop emitting reportMissingTypeStubs when importing fastapi_startkit). It enforces nothing in this repo's own CI.
  • check_types.py solves an orthogonal problem: turning this repo's own advisory pyright job into a blocking one without breaking CI on the pre-existing 659 errors. These two changes are independently justified; neither makes the other unnecessary.

Could the gate be simpler (inline YAML / pyproject / plain pyright CLI)?

  • Plain uv run pyright (no gate) isn't viable yet — it would fail immediately on the 659 pre-existing basic-mode errors documented in the PR.
  • Pyright has no built-in baseline/ratchet feature (unlike e.g. ESLint's warning caps), so "enforce only regressions" can't be expressed via pyright config or pyproject.toml alone.
  • An inline bash/jq one-liner in the YAML could replace the script, but it trades a small (64-line), typed, locally-runnable, single-purpose script for a less readable/testable shell fragment embedded in YAML, with no real reduction in what's being enforced. The script is also runnable locally via uv run python scripts/check_types.py, matching CI exactly.
  • Verdict: keep the script — it's the simplest reasonable way to implement the stated ratchet behavior, and it's correctly wired (working-directory: fastapi_startkit, baseline path resolved via Path(__file__).with_name(...) so it's CWD-independent).

Verification performed:

  • uv run python scripts/check_types.py on the PR branch → "OK: pyright errors at baseline (659)."
  • Directly ran uv run pyright --outputjsonsummary.errorCount == 659, confirming the script reads the right field and the baseline file is accurate.
  • Confirmed uv's own stderr warnings (e.g. VIRTUAL_ENV mismatch noise) do not leak into stdout, so proc.stdout.find("{") reliably locates the JSON blob in practice.

Minor, non-blocking suggestions:

  1. proc.stdout.find("{") JSON-extraction heuristic is slightly fragile in theory; empirically safe since uv's warnings go to stderr and pyright's --outputjson writes pure JSON to stdout. Could harden later with plain json.loads(proc.stdout).
  2. No unit test for check_types.py itself (covering the >, <, == branches). First script under scripts/, so no existing convention violated, but worth adding for future-proofing.

Neither blocks approval.

Add a PEP 561 py.typed marker so downstream type checkers honour the
package's inline hints instead of reporting reportMissingTypeStubs when
importing fastapi_startkit.
@tmgbedu
tmgbedu force-pushed the task/py-typed-static-gate-1053 branch from b44607b to 4c0a87d Compare July 16, 2026 08:08
@tmgbedu
tmgbedu merged commit ef3cb7a into main Jul 16, 2026
5 checks passed
@tmgbedu
tmgbedu deleted the task/py-typed-static-gate-1053 branch July 16, 2026 08:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant