Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ name: CI
# way. If you rename it, update the ruleset in the same change.
#
# So the two triggers do different jobs: `pull_request` GATES the merge, `push` VERIFIES the result
# of it. `release.yml` remains the gate that matters for money -- it re-runs `ruff` + `pytest`
# of it. `release.yml` remains the gate that matters for money -- it re-runs `ruff` + `mypy` + `pytest`
# itself before it will build an artifact, so a red `main` cannot become a wheel, a tag, or a
# release asset even if both of these were removed.
on:
Expand Down Expand Up @@ -60,6 +60,18 @@ jobs:
- name: Lint
run: uv run ruff check keel tests packages

# A STEP in this job, deliberately not a job of its own. The `main` ruleset requires the
# status context `test`, which this job produces; a separate `typecheck:` job would report
# a context nothing requires, so a red mypy would not block a merge -- the same failure
# shape the header above describes for a renamed job. As a step it inherits that gate.
#
# Paths come from `[tool.mypy]`'s `files` in pyproject.toml and are deliberately NOT
# repeated here: `keel.*` came off the `ignore_errors` list in #266, and the point of this
# step is that it cannot drift back silently. A package moving in or out of the checked
# set is then one edit there, not one edit mirrored across two workflows.
- name: Type-check
run: uv run mypy

- name: Test
run: uv run pytest -q

Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ jobs:
- name: Lint
run: uv run ruff check keel tests packages

# Re-run here rather than trusting CI's: this workflow is dispatched against whatever
# `main` is at the time, which need not be the commit any CI run went green on.
- name: Type-check
run: uv run mypy

- name: Test
run: uv run pytest -q

Expand Down
26 changes: 26 additions & 0 deletions tests/test_packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,32 @@ def test_the_strict_module_list_is_not_empty():
)


def test_keel_is_not_exempt_from_type_checking():
"""`keel.*` must not reappear on an `ignore_errors` override.

CI runs `mypy` (`ci.yml`, `release.yml`), which catches a type ERROR in `keel/`. It cannot
catch the other direction: re-adding `keel.*` here silences the whole package, mypy goes
green, and the ungating done in #266 is undone with nothing to show for it. That is the
failure mode this file already guards for strictness, in the opposite direction -- an
exemption that reads as a passing build.

`tests.*` and `keel_core.*` are still legitimately exempt (see the comments beside each in
pyproject.toml); this pins only the one that was deliberately brought under the checker.
"""
exempt = []
for override in _mypy_overrides():
if not override.get("ignore_errors"):
continue
entry = override["module"]
exempt.extend([entry] if isinstance(entry, str) else entry)

assert "keel.*" not in exempt, (
"`keel.*` is back on an `ignore_errors` override, which silently un-checks the entire "
"package -- mypy will pass while checking nothing there. It was ungated deliberately "
f"(#266); currently exempt: {sorted(exempt)!r}"
)


def test_broker_strict_flags_match_mypy_strict():
"""The expanded flag list must stay equal to what `--strict` actually turns on.

Expand Down
Loading