Repository navigation
Enable ruff argument, idiom and pytest-style rules (ARG, B, C4, FBT, PERF, PLW, PT, PTH, RUF, SIM and others) - #802
Merged
Merged
Conversation
anth-volk
added this pull request to stack #755
October 14, 2026 15:08
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
from
October 14, 2026 17:29
cf4b8b2 to
e25c130
Compare
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
from
October 14, 2026 17:56
e25c130 to
295754f
Compare
2 tasks done
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
3 times, most recently
from
October 14, 2026 21:06
6c08e78 to
75eaed8
Compare
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
2 times, most recently
from
October 14, 2026 21:52
3394d20 to
ef914a0
Compare
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
2 times, most recently
from
October 15, 2026 15:09
1a201e2 to
8023ea2
Compare
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
from
October 15, 2026 16:06
8023ea2 to
b672480
Compare
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
5 times, most recently
from
October 16, 2026 05:12
e238b8e to
6e33387
Compare
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
3 times, most recently
from
October 16, 2026 06:31
1168a56 to
9b13c69
Compare
2 tasks done
- Assign spm directly instead of through setattr (B010), use dict.fromkeys (C420), list unpacking (RUF005), one combined with-statement in the same enter/exit order (SIM117), a membership test (SIM109), merged upper-casing branches (SIM114), extend over an append loop (PERF401), and Path.open/Path.unlink (PTH). - Keep, with reasons: the per-child and per-call try/except loops (PERF203), the credentials temp file that stays open across the yield (SIM115), HDFStore.keys(), which isn't a dict view (SIM118), the FastAPI lifespan hook's app parameter (ARG001), and the precompute local entrypoint's positional force flag, whose signature Modal turns into the CLI (FBT001/FBT002). - Keep the telemetry and best-effort-cancel try/except blocks, whose except lines carry the BLE001/S110 reasons, and suppress SIM105 on their try lines. - Fixtures: prefix the identity stubs' positional-only parameters and the fake local_entrypoint's **kwargs with an underscore (ARG). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Request side-effect fixtures with @pytest.mark.usefixtures, and drop an unused monkeypatch request (ARG001/ARG002). - Prefix unused parameters of monkeypatched replacements with an underscore, after checking that every caller passes them positionally, and rename unused *args/**kwargs to *_args/**_kwargs (ARG001/ARG005). - Keep, with reasons, parameters that callers pass by keyword: the _load_dataset and _reduce replacements, the fake _build_simulation and economic_impact_analysis keyword-only parameters, FunctionCall.get stand-ins' timeout, and the Stage 12 writer stand-ins' prefix. Fake model versions that duck-type .py's MicrosimulationModelVersion keep their parameters too. - Mark the failing child store's create_or_resolve_simulation with @OverRide. - Use tuples for parametrize names (PT006). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Pass booleans by keyword: DatasetSelection's is_default (FBT003), and keyword-only routed/accepted test parameters, the routed helper flag, the output-builder helper's options and the modal.Dict stand-ins' environment_name/create_if_missing, matching Modal (FBT001/FBT002). - Match the real messages in pytest.raises (PT011); one native-only check keeps its broad ValueError, with a reason, because the rejecting layer's message varies. Make regex patterns raw or escaped (RUF043), give pytest.fail a message (PT016), and move the GCP credential cleanup check into a helper so pytest.raises wraps one call (PT012/SIM117). - Use Path methods (PTH), literals and dict() (C408/C416), a list literal (SIM905), unpacking (RUF005), extend (PERF401), ClassVar annotations (RUF012), an underscore for an unused unpacked value (RUF059), explicit check=False (PLW1510) and an ASCII operator in a comment (RUF003). - Pass regions.get, checks.append and nullcontext directly instead of through lambdas; nothing patches them (PLW0108). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The package update script stops overwriting its loop variable (PLW2901) and replaces the pyproject through Path.replace (PTH105). - The integration conftest uses bare @pytest.fixture (PT001). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds ARG, ASYNC, B, C4, FBT, PD, PERF, PGH, PLW, PT, PTH, RUF and SIM to the selected rule families. Every finding outside the shared rules tool is fixed or carries an inline reason. Complexity (C90, PLR) is left for group 8b. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sim.quality.ruff-lint now lists ARG, ASYNC, B, C4, FBT, PD, PERF, PGH, PLW, PT, PTH, RUF and SIM. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared scripts/rules tool takes the Public API's group 8a fixes: comprehensions, explicit check=False, bound loop variables and the removed unused workflow-parser parameter. The rendered rules page picks up the tool's new hash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#743 and #744 landed on main after this stack branched. Group 8a, which this PR enables, flags their new code; each fix follows this PR's conventions: - F821: this PR's CI-script commit dropped the package updater's os import, but #744's verify-dispatch reads os.environ, so restore it. - PTH123: the routing and sentinel CLIs open their files through Path. - RUF100: the sentinel's report helper prints with an explicit file, so its T201 suppression is unused. - ARG: tests request serialized_workflow through usefixtures (and the patched_modal fixture keeps it with a reason), drop unused monkeypatch requests, and prefix unused parameters of positional stand-ins. - FBT002: the routing-plan test helper takes force by keyword. - PT012/PT018/B905/RUF043: one call per pytest.raises, one condition per assert, an explicit non-strict zip over call chains of different lengths, and a raw match pattern. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#789 landed on main after this stack branched. Group 8a, which this PR enables, flags its new code; each fix follows this PR's conventions: - ARG001/ARG002: open_connection_once and the telemetry-recording test runtime keep keyword parameters their protocols pass, with reasons; the test's random.uniform stand-in prefixes its unused positional bound. - PERF203: the connection retry loop catches each refused attempt by design, with a reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#787 landed on main after this stack branched. Group 8a, which this PR enables, flags its new code; each fix follows this PR's conventions: - ARG002: the shared in-memory object store keeps upload_bytes's content_type, which Stage12ArtifactStore passes by keyword, with the reason this PR gave the per-file fake it replaced. - PT011: the invalid SPM provenance test expects pydantic's ValidationError, which every rejected value raises, not any ValueError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#795 landed on main after this stack branched. Group 8a, which this PR enables, flags its new code; each fix follows this PR's conventions: - ARG002: the child-deadline tests take the bare_country fixture through pytest.mark.usefixtures, like the runner tests. - ARG005: their _reduce stand-ins keep country_module, which run() passes by keyword, with the same reason. - ARG001: _crashing_call prefixes the clock it takes only to match _stuck_call's signature. - PT006: the child-timeout telemetry test names its parameters in a tuple. - SIM105: _cancel_call keeps its try/except, whose except line carries the BLE001/S110 reason, like the best-effort cancel it replaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
enables, flags its new tests; each fix follows this PR's conventions: - ARG002: its fake Modal calls, recording runtimes and rejecting aggregate writer keep the timeout, status_code and prefix keywords of the interfaces they stand in for, with the reasons this PR gave the other fakes. - PT011: the stage-and-role summary test matches the message it raises rather than accepting any ValueError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#758 landed on main after this stack branched. Group 8a, which this PR enables, flags its new code; each fix follows this PR's conventions: - B008: CallerAuthorizer.require_permission builds the authenticate dependency marker once and reads it from the nested dependency's default. The Annotated form can't be used: the module postpones annotations, and FastAPI resolves them against the module's globals, which don't hold the authorizer's dependency. FastAPI sees the same dependency, so each route's permission check is unchanged. - SIM105: the permission-missing event keeps its try/except, whose except line carries the BLE001/S110 reason, like the module's other telemetry. - ARG: the permission tests' fake decoders explain their unused parameters and the _decoder replacements prefix theirs; each table-driven test underscores the PROTECTED_ROUTES column it doesn't read, so the test ids stay the same. - ARG001: the deployed enforcement test requests permission_enforcement through pytest.mark.usefixtures; pytest still sets it up first, so the test skips before fetching a token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#781 landed on main after this stack branched. Group 8a, which this PR enables, flags its new code; each fix follows this PR's conventions: - ARG: stand-ins whose callers pass a parameter positionally prefix it with an underscore (the progress store's reads and writes, the patched Simulation.ensure, _country_module, get_region, Function.from_name, the preempted _set_modal_call_attributes, time.sleep and the on_first_ensure stand-ins), and unused *args/**kwargs become *_args/**_kwargs. Parameters callers pass by keyword keep a reason: the impact-analysis, _load_dataset, dispatch, run_simulation_impl and _configure_modal_observability stand-ins, the spawn stand-in's observability_context, the _reduce stand-in's country_module, the fake FunctionCall.get's timeout and the generated client's sync_detailed. - ARG001/ARG002: the contract's missing-record test and the bundle manifest test request their fixtures through pytest.mark.usefixtures. - B018: the failed-analysis test assigns the property it reads inside pytest.raises. - SIM105: the Modal app's stage-duration telemetry keeps its try/except, whose except line carries the BLE001/S110 reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test fakes and stubs mirror real signatures, because callers pass those arguments by keyword: run() passes country_module=, the runtime passes keyword-only options, and stand-ins replace modal.FunctionCall.get and JWTDecoder.__call__. Fixtures also request other fixtures only for their side effects. Tests are among the files allowed file-level exceptions, so ARG001, ARG002 and ARG005 join the tests' and the deployed-service tests' exemptions. That removes 72 inline suppressions from 16 test files. Application code keeps its six unused-argument suppressions and their reasons. Comments and configuration only: no code changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nine blocks ran a best-effort call and swallowed any error with try/except Exception: pass: - telemetry context and events in the entry app, its auth, the Modal app and the gateway; - a Modal call cancel during segmented national cleanup. Each block carried three suppressions: SIM105 on the try, and BLE001 and S110 on the except. Each is now with contextlib.suppress(Exception), with the old reason as a comment above it. It suppresses the same exceptions and runs the same calls, so behaviour is unchanged. That removes 18 suppression lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#712's segmentation and cache code raised 16 group 8a findings. Each is fixed in the code, with no new suppression: - PERF203: the policy-period parser and the segment-cancellation loop move their try/except into a helper called once per item. The parser's error helper is now typed NoReturn, since it always raises. The cancellation helper keeps #712's BLE001 reason. - RUF022: stage12_runtime's __all__ is sorted. - Tests: subprocess.run states check=False (PLW1510), match= patterns are raw or escape a literal dot (RUF043), compound asserts are split (PT018), the import guard extends its list from a generator (PERF401), boolean helper and parametrized arguments are keyword-only (FBT001/FBT002), and the segment-function stand-in is an instance whose spawn method takes Modal's observability_context by keyword (ARG004), as modal.Function.from_name returns an object. The rebase onto #712 also dropped this branch's PERF203 suppression in coordination.py, since #712 replaced the loop it was on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
force-pushed
the
ruff-idioms-arguments
branch
from
October 19, 2026 19:40
64ee75e to
4b79db8
Compare
This was referenced Oct 19, 2026
Closed
Ruff reads no project's requires-python here, so it linted the executor with its default target version. PERF203 applies only below Python 3.11, so the retry loop in database_retry.py needed a suppression that the executor's own Python (3.13) doesn't call for. Add the executor to per-file-target-version as py313, remove that suppression, and fix the eight findings the new target version reports: tomllib is now sorted with the standard library (I001, five files), and datetime.timezone.utc becomes datetime.UTC (UP017, three files). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- NoOpObservability now subclasses the Observability protocol it already implemented, and span() is marked @OverRide, which ruff exempts from ARG002. - The executor's in-memory object store records each object's content type, and the gateway's mock FunctionCall records the timeout each get() was called with, so both fakes use the arguments they mirror. - The gateway's import_gateway_models fixture only requested the module isolation fixture for its side effect. The test that used it now requests isolation with usefixtures and imports the models module itself, named in GatewayImportModuleNames.models. - The executor's FastAPI lifespan takes _app: Starlette and FastAPI pass the app positionally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Modal 1.4.2 builds a command-line option for every non-variadic parameter of a local entrypoint and calls it with keyword arguments, so a keyword-only force still gives --force/--no-force. The test already calls main(force=True). This removes the FBT001/FBT002 suppression. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- setup_gcp_credentials enters the credentials temp file on an ExitStack held by a with statement around the yield. The file still stays open across the yield; the finally block restores GOOGLE_APPLICATION_CREDENTIALS first, and the stack then closes, and so deletes, the file, the same order as before. - The UK local-authority check calls HDFStore.keys(include="pandas"), the default spelled out, and keeps a comment on why `in store` differs. - The native SPM test passes check= to pytest.raises (pytest 8.4), so the typed error code is part of the raises check instead of a later assert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The contextlib.suppress change rewrote "# pragma: no cover" as the prose "# Pragma: no cover". Put back the original lower-case spelling. The comment sits on its own line above the with statement, so coverage.py excludes no statement because of it either way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #801
Same-Repository Draft PR Check
PolicyEngine/policyengine-sim-api, not a personal fork.Summary
Phase 1 step 1.8, group 8a: ruff's argument, idiom and pytest-style rules. Bottom of the stack since #799 merged. Group 8b (#804) follows with the complexity refactors and the switch to
select = ["ALL"].Suppression counts
mainAgainst
main, this PR adds 2 suppression comments and removes 12:libs/policyengine-stage12-persistence/src/policyengine_stage12_persistence/store.py:157,ARG001("the ConnectionOpener protocol passes it by keyword"). This one is new; see "Suppressions this PR still adds".stage12_runtime/fanout.py:241,BLE001. This is not new: the existing suppression atfanout.py:236moved into the new per-call helper_cancel_segment_call.# noqa: BLE001, S110comments ontry: … except Exception: passblocks, which now usecontextlib.suppress(Exception)(see "Best-effort calls").BLE001comments, in the JWT decoder and the gateway's job-status handler. These handlers now re-raise withfrom <caught error>, which BLE001 accepts.fanout.py:236.Suppressions this PR still adds
store.py:157ARG001:open_connection_onceis the defaultConnectionOpener.operation_name=by keyword, and this implementation has no use for it.**_context: str, which accepts any keyword and so loosens the documented protocol.libs/*/srcdon't qualify for a file-level exception.Changes
scripts/rules/tests/**is exempt from ruff's pytest-style rules (PT), registered assim.quality.unittest-ruff-exemptions.ruff.tomlgainsARG,ASYNC,B,C4,FBT,PD,PERF,PGH,PLW,PT,PTH,RUFandSIM, one commit per project.requires-python. Every project and library file was therefore linted with ruff's default target version (below 3.11), although the executor requires Python 3.13.per-file-target-versionnow listsprojects/policyengine-simulation-executor/**aspy313.PERF203suppression indatabase_retry.pyunnecessary: ruff applies PERF203 only below Python 3.11.I001(tomllibnow sorts with the standard library) and 3UP017(datetime.UTC).usefixtures, and unused*args/**kwargsbecome*_args/**_kwargs.lifespan(_app), which Starlette and FastAPI call positionally._build_simulation,_load_dataset, and stand-ins for Modal'sFunctionCall.get(timeout=...)andJWTDecoder.__call__.ARG001,ARG002andARG005join the file-level exceptions for tests and deployed-service tests.NoOpObservabilitysubclasses theObservabilityprotocol it implements, andspan()is marked@override.FunctionCallrecords the timeout of eachget().import_gateway_modelsfixture, which only requested the isolation fixture for its side effect, is gone. Its test usesusefixturesand imports the models module directly.JWTDecoder.__init__'sauto_erroris keyword-only; its only caller already passed it by keyword.modal.Dict.from_name(..., *, create_if_missing).local_entrypointtakesmain(*, force=False).--forceand--no-forceare unchanged..github/scripts/modal-precompute.shalready passes--forceas an option.JWTDecoder.__call__and the gateway'srequire_authuseAnnotated[..., Depends(...)], so their token parameter no longer has a default; every caller passes one.CallerAuthenticator.__call__keeps a module-levelDependsmarker, because FastAPI can't resolveAnnotatedon a callable instance in a module with postponed annotations.from <caught error>.check=Falseonsubprocess.run, function references instead of lambdas where nothing patches the target (PLW0108),ClassVarannotations and raw regex patterns (RUF), and pytest idioms (PT001, PT006, PT011 withmatch=, PT017, PT018).contextlib.ExitStackheld by awithstatement around theyield.yield. Thefinallyblock still restoresGOOGLE_APPLICATION_CREDENTIALSbefore the file is closed and deleted.HDFStore.keys(include="pandas"), which is the default call spelled out.in storewould also match HDF nodes that aren't pandas tables, so the explicitkeys()call stays.check=topytest.raises(pytest 8.4), so the typed error code is part of the raises check.try: … except Exception: passnow usewith contextlib.suppress(Exception):. The old reason is a comment above each.# noqa: BLE001, S110comments (18 rule codes) that exist onmain.SIM105was not selected onmain, so noSIM105suppressions existed to remove.Exception. Ruff's BLE001, S110 and SIM105 do not checkcontextlib.suppress, so these nine catch-all blocks are not counted as suppressions.# pragma: no coveras# Pragma: no cover; the lower-case spelling is restored. The comment sits on its own line above thewithstatement, so coverage.py excludes no statement because of it.src/modal/app.py, two best-effort blocks becomecontextlib.suppressandimport contextlibis added; it gains no__future__import.sim.quality.ruff-lintnow lists the 13 families. The rules tool is synced from the Public API's group 8a (Enable ruff argument, idiom and pytest-style rules (ARG, B, C4, FBT, PERF, PLW, PT, PTH, RUF, SIM and others) policyengine-api#3924). Both test-exemption rules gainARG001,ARG002andARG005.Commits added on 2026-10-20 (85f690c)
per-file-target-version, thePERF203suppression removed, and the 8I001/UP017fixes.FBT001/FBT002.Testing
At 85f690c, each project in an environment synced with
uv sync --frozen --extra test --python 3.13:test_canonical_spm_native.pyare skipped withoutSPM_NATIVE_SMOKE_SOURCE, so thecheck=change there wasn't exercised. The integration tests need live services.check --extend-select RUF100 .andformat --check projects/*/src libs/*/srcare clean.scripts/run_quality_guards.py, with the basemain, pass. Rendering the rules leaves no diff.setup_gcp_credentialsreadsGOOGLE_APPLICATION_CREDENTIALS,GOOGLE_APPLICATION_CREDENTIALS_JSON,GCP_CREDENTIALS_JSON,GOOGLE_CREDENTIALSandSERVICE_ACCOUNT_JSON, as before. The executor functions attach thegcp-credentialsModal secret, and Stage 12 workers attachstage12-evaluation-gcp-credentials. Both secrets exist; which keys they hold wasn't checked.POLICYENGINE_ARTIFACT_BUCKET, from the repository variable that the deploy workflow's "Run artifact precompute" step sets.main.py, the two Modal utility programs,database_retry.pyand the gateway auth comment.🤖 Generated with Claude Code