Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
42fef0a
Exempt the rules tool's unittest tests from pytest-style rules
anth-volk Oct 14, 2026
95faba1
Use literals and comprehensions (C4, PERF)
anth-volk Oct 14, 2026
a4ac390
Simplify conditions and context managers (SIM)
anth-volk Oct 14, 2026
427efa7
Apply the RUF idioms (RUF)
anth-volk Oct 14, 2026
aa03103
Use pytest idioms in the tests (PT)
anth-volk Oct 14, 2026
8034412
Handle unused arguments in the application (ARG)
anth-volk Oct 14, 2026
80e6881
Handle unused arguments in the tests (ARG)
anth-volk Oct 14, 2026
f752674
Make boolean flags keyword-only (FBT)
anth-volk Oct 14, 2026
bbd5736
Fix bugbear and pylint warnings (B, PLW)
anth-volk Oct 14, 2026
6e9d795
Use pathlib and explain the kept pandas and async calls (PTH, PD, ASYNC)
anth-volk Oct 14, 2026
64b6d64
Enable ruff group 8a (arguments, idioms and pytest style)
anth-volk Oct 14, 2026
9e45536
Register group 8a in the ruff-lint rule
anth-volk Oct 14, 2026
b67f7a5
Fix the group 8a findings in the application code #3891 added
anth-volk Oct 15, 2026
7d1d853
Request the logger fixtures #3916's tests need only for their side ef…
anth-volk Oct 15, 2026
7352ec0
Apply the group 8a idioms to the tests #3891 added
anth-volk Oct 15, 2026
790a260
Exempt test stand-ins from unused-argument rules file-wide
anth-volk Oct 16, 2026
3e6b434
Keep process state in objects instead of rebinding module globals (PL…
anth-volk Oct 20, 2026
ffbdc7d
Mark method overrides with typing_extensions.override (ARG002)
anth-volk Oct 20, 2026
fabd518
Write the non-ASCII test digits as escape sequences (RUF001)
anth-volk Oct 20, 2026
93a6c6d
Read the load generator's payload before starting the event loop (ASY…
anth-volk Oct 20, 2026
5ae76f9
Apply this PR's rules to the code #3985 added (PTH123, PERF401, C420)
anth-volk Oct 20, 2026
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
3 changes: 2 additions & 1 deletion .github/find-api-model-versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import shlex
import sys
from importlib.metadata import version as distribution_version
from pathlib import Path

from policyengine.bundle import get_current_bundle

Expand Down Expand Up @@ -97,7 +98,7 @@ def find_api_model_versions() -> dict[str, str]:
def find_api_model_versions_and_output_to_github() -> None:
"""Find the API model versions and output them to a file for GitHub."""
versions = find_api_model_versions()
with open(os.environ["GITHUB_ENV"], "a") as f:
with Path(os.environ["GITHUB_ENV"]).open("a") as f:
for key, value in versions.items():
f.write(f"{key}={value}\n")

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ To add or change a rule, read `docs/engineering/skills/rules-registry.md`.

### Code quality and types

- Checked: `api.quality.alembic-revisions-unused-imports`, `api.quality.ci-cd-scripts-ruff-exemptions`, `api.quality.command-line-modules-ruff-exemptions`, `api.quality.command-line-scripts-ruff-exemptions`, `api.quality.environment-variables-load`, `api.quality.mypy-v2`, `api.quality.ruff-format`, `api.quality.ruff-lint`, `api.quality.runtime-evaluated-models-resolve`, `api.quality.tests-pass`, `api.quality.tests-ruff-exemptions`
- Checked: `api.quality.alembic-revisions-unused-imports`, `api.quality.ci-cd-scripts-ruff-exemptions`, `api.quality.command-line-modules-ruff-exemptions`, `api.quality.command-line-scripts-ruff-exemptions`, `api.quality.environment-variables-load`, `api.quality.mypy-v2`, `api.quality.ruff-format`, `api.quality.ruff-lint`, `api.quality.runtime-evaluated-models-resolve`, `api.quality.tests-pass`, `api.quality.tests-ruff-exemptions`, `api.quality.unittest-ruff-exemptions`

### Observability

Expand Down
1 change: 1 addition & 0 deletions changelog.d/ruff-idioms-arguments.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enabled ruff's argument, idiom and pytest-style rules (ARG, ASYNC, B, C4, FBT, PD, PERF, PGH, PLW, PT, PTH, RUF, SIM): unused arguments are removed, underscored or explained (test stand-ins that mirror a real signature take ARG001, ARG002 and ARG005 as file-level test exceptions instead), tests request side-effect fixtures with usefixtures and match the exception messages they expect, boolean options of internal helpers are keyword-only, and re-raised errors keep their cause; published schemas and the HTTP API are unchanged, and the shared rules tool's unittest tests are exempt from pytest-style rules.
25 changes: 15 additions & 10 deletions docs/generated/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ Every rule that governs `policyengine-api` is listed here with how it is enforce

| Tier | enforced | ratcheted | advisory | doc-only | Total |
| --- | ---: | ---: | ---: | ---: | ---: |
| mechanical | 50 | 0 | 1 | 12 | 63 |
| mechanical | 51 | 0 | 1 | 12 | 64 |
| metric | 0 | 0 | 0 | 0 | 0 |
| judgment | 0 | 0 | 0 | 1 | 1 |

**Blocking:** 50 of 64 rules.
**Blocking:** 51 of 65 rules.

## Gaps

Expand Down Expand Up @@ -296,10 +296,11 @@ flowchart LR
| `api.quality.environment-variables-load` | Required environment variables and credentials load in a deployed-like environment. | mechanical | enforced | yes | CI job `test_env_vars` | `pr.yml:test_env_vars` | — |
| `api.quality.mypy-v2` | Shared query models, FastAPI dependencies, v2 routes and services, and the user sign-in and account-deletion libraries pass mypy with untyped definitions disallowed. | mechanical | enforced | yes | mypy | `pr.yml:typecheck-v2` | — |
| `api.quality.ruff-format` | All Python files are formatted with ruff format. | mechanical | enforced | yes | ruff format | `pr.yml:lint` | — |
| `api.quality.ruff-lint` | All Python code passes ruff check with every rule family selected in pyproject.toml, with no ratchet; families are added one themed group at a time until the selection is ALL. | mechanical | enforced | yes | ruff check (A, ANN, BLE, D, DTZ, E, E4, E7, E9, EM, ERA, EXE, F, FAST, FIX, FLY, FURB, G, I, INP, LOG, N, PIE, PLC, PYI, RET, RSE, S, SLF, T20, TC, TD, TID253, TRY, UP, W) | `pr.yml:lint` | — |
| `api.quality.ruff-lint` | All Python code passes ruff check with every rule family selected in pyproject.toml, with no ratchet; families are added one themed group at a time until the selection is ALL. | mechanical | enforced | yes | ruff check (A, ANN, ARG, ASYNC, B, BLE, C4, D, DTZ, E, E4, E7, E9, EM, ERA, EXE, F, FAST, FBT, FIX, FLY, FURB, G, I, INP, LOG, N, PD, PERF, PGH, PIE, PLC, PLW, PT, PTH, PYI, RET, RSE, RUF, S, SIM, SLF, T20, TC, TD, TID253, TRY, UP, W) | `pr.yml:lint` | — |
| `api.quality.runtime-evaluated-models-resolve` | Every package model is one ruff treats as runtime-evaluated (a base resolves, through the import in its own module, to a name in runtime-evaluated-base-classes, or is a same-module class that does), and every Pydantic model and SQLAlchemy mapper resolves its annotations once the package is imported. | mechanical | enforced | yes | pytest `tests/unit/test_runtime_evaluated_annotations.py` | `pr.yml:test` | **unproven** |
| `api.quality.tests-pass` | The unit, contract and to-refactor test suites pass. | mechanical | enforced | yes | CI job `test` | `pr.yml:test` | — |
| `api.quality.tests-ruff-exemptions` | Test files may use pytest's bare assert, need no docstrings, annotations or named constants for magic numbers, and may reach into private members, live outside packages and import inside a test (S101, D1, ANN, PLR2004, SLF001, INP001, PLC0415). Their fakes may name methods after the interface they replace, such as a gRPC stub's Export (N802). They may also use fake credentials, naive datetimes, subprocess calls to repository scripts, SQL built from strings and prints (S105, S106, DTZ001, DTZ011, S603, S607, S608, T201), and may import the lazily loaded packages at module level (TID253). Every other ruff rule applies to tests. | mechanical | enforced | yes | ruff per-file ignore of ANN, D1, DTZ001, DTZ011, INP001, N802, PLC0415, PLR2004, S101, S105, S106, S603, S607, S608, SLF001, T201, TID253 for `**/tests/**` | `pr.yml:lint` | — |
| `api.quality.tests-ruff-exemptions` | Test files may use pytest's bare assert, need no docstrings, annotations or named constants for magic numbers, and may reach into private members, live outside packages and import inside a test (S101, D1, ANN, PLR2004, SLF001, INP001, PLC0415). Their fakes may name methods after the interface they replace, such as a gRPC stub's Export (N802). They may also use fake credentials, naive datetimes, subprocess calls to repository scripts, SQL built from strings and prints (S105, S106, DTZ001, DTZ011, S603, S607, S608, T201), may import the lazily loaded packages at module level (TID253), and may leave a stand-in's arguments unused (ARG001, ARG002, ARG005). Every other ruff rule applies to tests. | mechanical | enforced | yes | ruff per-file ignore of ANN, ARG001, ARG002, ARG005, D1, DTZ001, DTZ011, INP001, N802, PLC0415, PLR2004, S101, S105, S106, S603, S607, S608, SLF001, T201, TID253 for `**/tests/**` | `pr.yml:lint` | — |
| `api.quality.unittest-ruff-exemptions` | The shared rules tool's tests use unittest, because the tool needs only the standard library, so ruff's pytest-style rules (PT) don't apply to them. | mechanical | enforced | yes | ruff per-file ignore of PT for `scripts/rules/tests/**` | `pr.yml:lint` | — |

```mermaid
flowchart LR
Expand All @@ -321,14 +322,16 @@ flowchart LR
r_api_quality_ruff_format("api.quality.ruff-format (enforced)")
e_ruff_format["ruff format"]
r_api_quality_ruff_lint("api.quality.ruff-lint (enforced)")
e_ruff_check__A__ANN__BLE__D__DTZ__E__E4__E7__E9__EM__ERA__EXE__F__FAST__FIX__FLY__FURB__G__I__INP__LOG__N__PIE__PLC__PYI__RET__RSE__S__SLF__T20__TC__TD__TID253__TRY__UP__W_["ruff check (A, ANN, BLE, D, DTZ, E, E4, E7, E9, EM, ERA, EXE, F, FAST, FIX, FLY, FURB, G, I, INP, LOG, N, PIE, PLC, PYI, RET, RSE, S, SLF, T20, TC, TD, TID253, TRY, UP, W)"]
e_ruff_check__A__ANN__ARG__ASYNC__B__BLE__C4__D__DTZ__E__E4__E7__E9__EM__ERA__EXE__F__FAST__FBT__FIX__FLY__FURB__G__I__INP__LOG__N__PD__PERF__PGH__PIE__PLC__PLW__PT__PTH__PYI__RET__RSE__RUF__S__SIM__SLF__T20__TC__TD__TID253__TRY__UP__W_["ruff check (A, ANN, ARG, ASYNC, B, BLE, C4, D, DTZ, E, E4, E7, E9, EM, ERA, EXE, F, FAST, FBT, FIX, FLY, FURB, G, I, INP, LOG, N, PD, PERF, PGH, PIE, PLC, PLW, PT, PTH, PYI, RET, RSE, RUF, S, SIM, SLF, T20, TC, TD, TID253, TRY, UP, W)"]
r_api_quality_runtime_evaluated_models_resolve("api.quality.runtime-evaluated-models-resolve (enforced)")
e_pytest_tests_unit_test_runtime_evaluated_annotations_py["pytest tests/unit/test_runtime_evaluated_annotations.py"]
j_CI_pr_yml_test["CI pr.yml:test"]
r_api_quality_tests_pass("api.quality.tests-pass (enforced)")
e_CI_job_test["CI job test"]
r_api_quality_tests_ruff_exemptions("api.quality.tests-ruff-exemptions (enforced)")
e_ruff_per_file_ignore_of_ANN__D1__DTZ001__DTZ011__INP001__N802__PLC0415__PLR2004__S101__S105__S106__S603__S607__S608__SLF001__T201__TID253_for____tests___["ruff per-file ignore of ANN, D1, DTZ001, DTZ011, INP001, N802, PLC0415, PLR2004, S101, S105, S106, S603, S607, S608, SLF001, T201, TID253 for **/tests/**"]
e_ruff_per_file_ignore_of_ANN__ARG001__ARG002__ARG005__D1__DTZ001__DTZ011__INP001__N802__PLC0415__PLR2004__S101__S105__S106__S603__S607__S608__SLF001__T201__TID253_for____tests___["ruff per-file ignore of ANN, ARG001, ARG002, ARG005, D1, DTZ001, DTZ011, INP001, N802, PLC0415, PLR2004, S101, S105, S106, S603, S607, S608, SLF001, T201, TID253 for **/tests/**"]
r_api_quality_unittest_ruff_exemptions("api.quality.unittest-ruff-exemptions (enforced)")
e_ruff_per_file_ignore_of_PT_for_scripts_rules_tests___["ruff per-file ignore of PT for scripts/rules/tests/**"]
r_api_quality_alembic_revisions_unused_imports -.-> gap_unenforced
r_api_quality_ci_cd_scripts_ruff_exemptions --> e_ruff_per_file_ignore_of_N999__S310__T201__TID253_for__github___
e_ruff_per_file_ignore_of_N999__S310__T201__TID253_for__github___ --> j_CI_pr_yml_lint
Expand All @@ -342,14 +345,16 @@ flowchart LR
e_mypy --> j_CI_pr_yml_typecheck_v2
r_api_quality_ruff_format --> e_ruff_format
e_ruff_format --> j_CI_pr_yml_lint
r_api_quality_ruff_lint --> e_ruff_check__A__ANN__BLE__D__DTZ__E__E4__E7__E9__EM__ERA__EXE__F__FAST__FIX__FLY__FURB__G__I__INP__LOG__N__PIE__PLC__PYI__RET__RSE__S__SLF__T20__TC__TD__TID253__TRY__UP__W_
e_ruff_check__A__ANN__BLE__D__DTZ__E__E4__E7__E9__EM__ERA__EXE__F__FAST__FIX__FLY__FURB__G__I__INP__LOG__N__PIE__PLC__PYI__RET__RSE__S__SLF__T20__TC__TD__TID253__TRY__UP__W_ --> j_CI_pr_yml_lint
r_api_quality_ruff_lint --> e_ruff_check__A__ANN__ARG__ASYNC__B__BLE__C4__D__DTZ__E__E4__E7__E9__EM__ERA__EXE__F__FAST__FBT__FIX__FLY__FURB__G__I__INP__LOG__N__PD__PERF__PGH__PIE__PLC__PLW__PT__PTH__PYI__RET__RSE__RUF__S__SIM__SLF__T20__TC__TD__TID253__TRY__UP__W_
e_ruff_check__A__ANN__ARG__ASYNC__B__BLE__C4__D__DTZ__E__E4__E7__E9__EM__ERA__EXE__F__FAST__FBT__FIX__FLY__FURB__G__I__INP__LOG__N__PD__PERF__PGH__PIE__PLC__PLW__PT__PTH__PYI__RET__RSE__RUF__S__SIM__SLF__T20__TC__TD__TID253__TRY__UP__W_ --> j_CI_pr_yml_lint
r_api_quality_runtime_evaluated_models_resolve --> e_pytest_tests_unit_test_runtime_evaluated_annotations_py
e_pytest_tests_unit_test_runtime_evaluated_annotations_py --> j_CI_pr_yml_test
r_api_quality_tests_pass --> e_CI_job_test
e_CI_job_test --> j_CI_pr_yml_test
r_api_quality_tests_ruff_exemptions --> e_ruff_per_file_ignore_of_ANN__D1__DTZ001__DTZ011__INP001__N802__PLC0415__PLR2004__S101__S105__S106__S603__S607__S608__SLF001__T201__TID253_for____tests___
e_ruff_per_file_ignore_of_ANN__D1__DTZ001__DTZ011__INP001__N802__PLC0415__PLR2004__S101__S105__S106__S603__S607__S608__SLF001__T201__TID253_for____tests___ --> j_CI_pr_yml_lint
r_api_quality_tests_ruff_exemptions --> e_ruff_per_file_ignore_of_ANN__ARG001__ARG002__ARG005__D1__DTZ001__DTZ011__INP001__N802__PLC0415__PLR2004__S101__S105__S106__S603__S607__S608__SLF001__T201__TID253_for____tests___
e_ruff_per_file_ignore_of_ANN__ARG001__ARG002__ARG005__D1__DTZ001__DTZ011__INP001__N802__PLC0415__PLR2004__S101__S105__S106__S603__S607__S608__SLF001__T201__TID253_for____tests___ --> j_CI_pr_yml_lint
r_api_quality_unittest_ruff_exemptions --> e_ruff_per_file_ignore_of_PT_for_scripts_rules_tests___
e_ruff_per_file_ignore_of_PT_for_scripts_rules_tests___ --> j_CI_pr_yml_lint
```

## Observability
Expand Down
2 changes: 1 addition & 1 deletion migrations/v2/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def _include_application_object(
_object: SchemaItem,
name: str | None,
type_: NameFilterType,
_reflected: bool,
_reflected: bool, # noqa: FBT001 # Alembic calls include_object with positional arguments
_compare_to: SchemaItem | None,
) -> bool:
"""Keep Alembic's own version table outside application drift."""
Expand Down
6 changes: 3 additions & 3 deletions policyengine_api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def get_py_manifest() -> Any: # noqa: ANN401 # SimplePath, which open() reject

def _load_policyengine_bundle() -> dict | None:
try:
with open(get_py_manifest()) as manifest_file:
with Path(get_py_manifest()).open() as manifest_file:
manifest = json.load(manifest_file)
except Exception as exc:
msg = (
Expand Down Expand Up @@ -167,11 +167,11 @@ def get_bundle_default_dataset_option(country_id: str) -> dict:
}
COUNTRY_PACKAGE_VERSIONS = {
country: _resolve_distribution_version(_dist_versions, package_name)
for country, package_name in zip(COUNTRIES, COUNTRY_PACKAGE_NAMES)
for country, package_name in zip(COUNTRIES, COUNTRY_PACKAGE_NAMES, strict=True)
}
except Exception: # noqa: BLE001 # unreadable package metadata falls back to 0.0.0; import must not fail
_dist_versions = {}
COUNTRY_PACKAGE_VERSIONS = {country: "0.0.0" for country in COUNTRIES}
COUNTRY_PACKAGE_VERSIONS = dict.fromkeys(COUNTRIES, "0.0.0")

_policyengine_bundle = _load_policyengine_bundle()
_bundle_package_versions = _resolve_bundle_package_versions(_policyengine_bundle)
Expand Down
Loading
Loading