Skip to content

Enable ruff annotation rules (ANN) - #3905

Draft
anth-volk wants to merge 9 commits into
ruff-typing-importsfrom
ruff-annotations
Draft

anth-volk wants to merge 9 commits into
ruff-typing-importsfrom
ruff-annotations

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #3904

Summary

Phase 1 step 1.8, group 3: annotations. Stacked on #3902.

  • Annotations: select gains ANN, and every function outside tests is now annotated: 252 findings in policyengine_api and the scripts, plus 16 in .github/.

  • Any (ANN401): replaced by a real type wherever one exists (56 of 63). Types used include object for opaque pass-through values, the existing JSONValue alias, CachedReformImpact, Starlette Scope, ArrayLike, and two new Protocols (CachePipeline, SPMCapabilitySource).

  • Remaining suppressions: each keeps an inline # noqa: ANN401 with a reason:

    • exact-type() JSON validators mypy can't narrow;
    • Session.exec overloads that reject core selects;
    • dataclasses.replace(**changes);
    • the TOML value from Enforcer.get;
    • incompatible Flask/a2wsgi WSGI stubs;
    • pass-through JSON values;
    • Pydantic model-class maps.

    One noqa: ANN201 is on a Pydantic wrap serializer, because annotating it would change the published OpenAPI schema.

  • Test-file exemptions: **/tests/** ignores S101, D1, ANN and PLR2004, as agreed for step 1.8. This is registered as api.quality.tests-ruff-exemptions.

  • Annotations checked against the code: I ran mypy over the whole package (beyond the configured v2 scope) and compared it with the base branch. Annotations that didn't match the code were corrected in the commit "Correct annotations that whole-package mypy shows were wrong". Whole-package mypy errors fell from 259 to 182.

  • Eight new whole-package errors remain:

    • Library typing: policyengine_core has no py.typed, and Connector.connect and numpy arithmetic return Any.
    • Two real code gaps, now visible because the functions are annotated:
      • Flask's view_args is optional, but the route indexes it.
      • get_execution_result accepts None, but its success path needs an execution.

    Fixing either needs a code change, which is out of scope for an annotation PR.

Testing

  • pytest tests/unit tests/contract: 2,358 passed. to_refactor plus dedupe: 53 passed.
  • Configured mypy is clean. ruff check/format are clean repo-wide.
  • All 234 Pydantic model schemas and the OpenAPI spec are identical to the base branch.
  • Guards and the rules tool unittest pass. scripts/rules is byte-identical with sim.

🤖 Generated with Claude Code

@anth-volk
anth-volk added this pull request to stack #3888 October 13, 2026 12:35
@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.86364% with 46 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.38%. Comparing base (b71530a) to head (ca36072).

Files with missing lines Patch % Lines
policyengine_api/runtime_cache/core.py 22.22% 0 Missing and 14 partials ⚠️
policyengine_api/spm.py 66.66% 4 Missing and 1 partial ⚠️
policyengine_api/asgi_factory.py 62.50% 3 Missing ⚠️
policyengine_api/specification.py 57.14% 2 Missing and 1 partial ⚠️
...e_api/utils/payload_validators/validate_country.py 66.66% 2 Missing and 1 partial ⚠️
policyengine_api/worker_spm.py 57.14% 1 Missing and 2 partials ⚠️
policyengine_api/data/orm.py 50.00% 1 Missing and 1 partial ⚠️
policyengine_api/data/v2/catalog/extraction.py 60.00% 1 Missing and 1 partial ⚠️
...pi/fastapi_routes/v2/households/document_models.py 60.00% 1 Missing and 1 partial ⚠️
policyengine_api/libs/gateway_auth.py 60.00% 1 Missing and 1 partial ⚠️
... and 5 more
Additional details and impacted files
@@                   Coverage Diff                   @@
##           ruff-typing-imports    #3905      +/-   ##
=======================================================
- Coverage                85.61%   85.38%   -0.23%     
=======================================================
  Files                      198      198              
  Lines                    12343    12400      +57     
  Branches                  2245     2264      +19     
=======================================================
+ Hits                     10567    10588      +21     
- Misses                    1094     1111      +17     
- Partials                   682      701      +19     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@anth-volk
anth-volk removed this pull request from stack #3888 October 13, 2026 19:26
@anth-volk
anth-volk added this pull request to stack #3917 October 13, 2026 19:26
@anth-volk
anth-volk force-pushed the ruff-annotations branch 3 times, most recently from d635523 to 7ab94cc Compare October 14, 2026 16:59
anth-volk and others added 9 commits October 14, 2026 21:57
Type the cache backend protocol with what callers rely on (string
envelopes in, opaque write acknowledgements out) and add a CachePipeline
protocol for the queued writes. Decoded envelope payloads are object,
because every consumer validates them. A SPMCapabilitySource protocol
covers both the HTTP entrypoint client and the release registry adapter
that validate_worker_spm accepts. ReformImpactCache.update keeps Any for
the keyword changes it forwards unchanged to dataclasses.replace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reform-impact service parameters take the cache's scalar types, with the
options hash optional on the write paths that receive it from
EconomicImpactSetupOptions. Policy JSON read from the v1 table is a
JSONValue. SPMSelection.serialize_selection stays without a return
annotation (noqa ANN201) because Pydantic would build the model's
serialization JSON schema from it and change the published OpenAPI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before-validators take and return object. The parameter-tree SQL helpers
take mapped columns and return typed column expressions. Three helpers
keep Any with a reason: the JSON validators and canonical encoders
dispatch on exact type() checks mypy cannot narrow, and read_rows passes
core SQLAlchemy selects that Session.exec's overloads do not declare.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
View decorators are generic over the wrapped view's result, so a view
returning dict | Response keeps that type plus the error Response.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/rules stays standard-library only. Enforcer.get keeps Any for
the raw TOML value that callers read by its shape. scripts/rules changes
need mirroring in policyengine-sim-api.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Select ANN everywhere except tests, which need no annotations. The
annotation commits before this one fix every finding outside .github/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bump_version.py, find-api-model-versions.py and scripts/check_updates.py
(with a ChangelogEntry TypedDict). Annotations only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
api.quality.ruff-lint now includes ANN, and the **/tests/** per-file
ignore list (S101, D1, ANN, PLR2004) is registered as
api.quality.tests-ruff-exemptions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active (outdated) deployment
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.

Enable ruff annotation rules (ANN)

1 participant