Repository navigation
Enable ruff structure and import rules (INP, PLC, SLF, N, A, FAST, E) - #799
Merged
Merged
Conversation
anth-volk
added this pull request to stack #755
October 14, 2026 13:54
anth-volk
force-pushed
the
ruff-structure-imports
branch
2 times, most recently
from
October 14, 2026 14:47
2a7ddcc to
30b23b7
Compare
2 tasks done
anth-volk
force-pushed
the
ruff-structure-imports
branch
4 times, most recently
from
October 14, 2026 20:04
b013982 to
548762d
Compare
anth-volk
force-pushed
the
ruff-structure-imports
branch
4 times, most recently
from
October 15, 2026 14:06
51e611a to
fddf990
Compare
anth-volk
force-pushed
the
ruff-structure-imports
branch
2 times, most recently
from
October 15, 2026 16:06
4bd1b75 to
f523429
Compare
anth-volk
force-pushed
the
ruff-structure-imports
branch
from
October 15, 2026 21:56
f523429 to
5039fe2
Compare
anth-volk
force-pushed
the
ruff-structure-imports
branch
5 times, most recently
from
October 16, 2026 05:58
f773fd9 to
87a863d
Compare
Tests deliberately reach into private members, pytest test directories aren't packages, and tests often import inside a test after patching the environment. Application code still gets every rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FastAPI takes the response model from the return annotation when response_model is omitted, so the generated OpenAPI documents are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Alias the shared exit registry as app_exit where it is imported, and keep the policyengine_fastapi.exit.exit name that projects import. - Use snake_case for the OpenTelemetry provider and log-handler locals. - Add the Error suffix to exceptions that never leave their project: BackendUnavailableError, BackendTimeoutError, TemporaryStage12UnsupportedRequestError and ParityMismatchError. TemporaryStage12DispatchFailed keeps its name, which the entry app logs as error_type. - Keep the camelCase budget-window fields, which are published JSON, the explicit re-exports in stage12_execution, and the two deliberate private accesses, each with a reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Declare the script directories (.github/scripts, scripts and the executor's scripts) and the rules proof fixtures as namespace packages in ruff.toml. Ruff's full output, with every rule selected, is otherwise unchanged by the setting. The executor's src/modal/utils scripts keep an inline suppression instead: declaring that directory, or adding an __init__.py, makes ruff treat it as part of a first-party "modal" package and regroup its Modal SDK imports. An __init__.py would also change the src.modal source baked into the Modal images. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(ruff PLC0415) Each deferred import keeps an inline reason: the GCP exporters, logging instrumentation and metadata probe load only when used, and only the bundle-versions command runs where policyengine is installed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
app.py already imports stage12_backend at module level for its exception types, so binding Stage12ComparisonBackend there too loads nothing new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bind SPMSelection in endpoints.py's existing policyengine_simulation_contract.spm import. Keep the Modal function body's imports, which build the web app in its container, and the smoke app's in-image imports, each with a reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move only imports that load nothing new and that no test patches: pathlib and importlib, the policyengine_simulation_contract helpers, names from a module the file already imports at the top, and RegionGroupStrategy, which policyengine.core already loads. Every other deferred import keeps an inline reason: Modal function bodies and the image-build and smoke functions, lazily loaded policyengine models and heavy libraries, call-time resolution that the tests' patch seams rely on, and import cycles in the runtime. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split long messages and the GCP log format with implicit string concatenation, so every string is unchanged, and rewrap two docstrings that no published schema uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bind the long generated endpoint functions to shorter local names before calling them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split long strings with implicit concatenation, so every value is unchanged. The fake curl and gcloud scripts keep their single-line printf output with a suppression on the string. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split long messages and test strings with implicit concatenation, so every value is unchanged, and shorten docstrings that no published schema uses. The fake gh script keeps its single-line JSON output with a suppression on the string. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split the Modal app's FastAPI description with implicit concatenation, so the published text is unchanged, and shorten four test names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Select A, E, FAST, INP, N, PLC and SLF. Every finding outside the shared scripts/rules tool is fixed or carries an inline reason; the rules tool's findings are fixed upstream and synced separately. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sim.quality.ruff-lint now lists A, E, FAST, INP, N, PLC and SLF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shared scripts/rules tool takes the Public API's group 7 fixes: wrapped long lines, BaseUnavailableError, and assert_violation in its tests. 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 7, which this PR enables, flags their new code; each fix follows this PR's conventions: - E501: wrap the long strings in their routing, sentinel, gateway and test code; every message and expected value reads the same. - N818: RoutingConflict never leaves the executor, so it becomes RoutingConflictError, as this PR renames the other in-project errors. - INP001: the sentinel script in src/modal/utils keeps the inline reason the other scripts there carry. - PLC0415: the sentinel's deferred contract and runtime imports and the package updater's optional receipt import keep inline reasons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#789 landed on main after this stack branched. E501, which this PR enables, flags the signature line of its lost-commit coordinator test, whose name cannot be wrapped; the line keeps a reasoned suppression rather than a shorter, vaguer name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#791 landed on main after this stack branched. E501, which this PR enables, flags three long string literals in its Stage 12 failure-cause tests; each is wrapped by implicit concatenation, with its value unchanged. N818 flags Stage12FailureCause, whose name telemetry records as the error type of every recorded cause, so it keeps its name under a reasoned suppression, like TemporaryStage12DispatchFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#758 landed on main after this stack branched. FAST002, which this PR enables, flags the six entry routes that take the caller's identity as a "caller: CallerIdentity | None = Depends(...)" parameter. FastAPI's Annotated form can't be used: this module postpones annotations, and FastAPI resolves them against the module's globals, where the per-app permission dependency doesn't exist. No handler reads the parameter. Each route now declares its permission dependency in the decorator's dependencies, as the gateway's routes declare require_auth and as these routes declared authentication before #758. FastAPI still runs the same dependency before the body is read, so each route checks the same permission, logs or rejects a missing one the same way, and the OpenAPI document is unchanged. The CallerIdentity import is no longer used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#781 landed on main after this stack branched. PLC0415, which this PR enables, flags four of its deferred imports. As for the executor's other deferred imports, only an import that loads nothing new and that no test patches moves to the top: - the Modal app's NULL_JOB_PROGRESS and StoredJobProgressReporter, whose module the app's top-level imports already load, move to the top; - the job_progress_state store functions stay deferred, resolved per call so the tests can patch job_progress_state; - StageReportingSimulation stays deferred in _build_simulation, resolved per call like its neighbours so the tests can patch it; - the bundle-metadata builder's JOB_PROGRESS_CAPABILITY_VERSION stays deferred: the contract package, and pydantic with it, loads only when bundle metadata is built. Each kept import carries its reason inline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk
force-pushed
the
ruff-structure-imports
branch
from
October 16, 2026 06:06
87a863d to
19b613a
Compare
…import Fourteen heavy or optional packages and modules are only ever imported inside the function that needs them: the policyengine bundle, countries, provenance and tax-benefit-model modules, policyengine_us, spm_calculator, h5py, google.cloud, three OpenTelemetry exporters and instrumentations, pythonjsonlogger, and the executor's spm and baseline_artifacts modules. No application module imports any of them at module level. ruff.toml now lists them in banned-module-level-imports and selects TID253, so importing one at module level fails (TYPE_CHECKING imports are still fine), and PLC0415 accepts their in-function imports without a suppression. That removes 51 PLC0415 suppressions. Tests may still import them at module level (TID253 joins the test exemptions). Packages that some application module does import at module level (pandas, microdf, huggingface_hub, policyengine.core and others) aren't listed, so their deferred imports keep their reasons. The nine Modal utility programs, run with python -m from a directory that isn't a package, take INP001 in their existing file-level entry instead of one suppression each. Comments and configuration only: no code changes. 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 #798
Same-Repository Draft PR Check
PolicyEngine/policyengine-sim-api, not a personal fork.Summary
Phase 1 step 1.8, group 7: structure and imports. Bottom of the stack since #785 merged.
**/tests/**and**/authenticated_tests/**now also ignoreSLF001,INP001andPLC0415, alongsideS101,D1,ANNandPLR2004. Tests deliberately reach into private members, pytest test directories aren't packages, and tests often import inside a test after patching the environment. Application code still gets every rule. Both test-exemption rules in the registry are updated to match.ruff.tomlgainsA,E,FAST,INP,N,PLCandSLF. All 339 findings left after the exemption are fixed or kept with an inline reason.bundle,countries,provenanceandtax_benefit_modelsmodules, andpolicyengine_us;spm_calculator,h5py,google.cloudandpythonjsonlogger;spmandbaseline_artifactsmodules.banned-module-level-imports, withTID253selected. Importing one at module level now fails, thoughTYPE_CHECKINGimports are fine.PLC0415accepts their in-function imports without a suppression.TID253joins the test exemptions.huggingface_hub,policyengine.coreand others), and import cycles. Roadmap items cover the Modal imports (image.imports()) and the test seams..github/scripts,scripts, the executor'sscriptsandrules/fixturesaren't packages, so they're declared in ruff's top-levelnamespace-packagessetting. Compared with the fullruff checkoutput before the change, nothing else differs. The nine executorsrc/modal/utilsprograms takeINP001in their existing file-level entry (a program undersrc) instead of inline. Declaring that directory as a namespace package made ruff treatimport modal(the Modal SDK) as first-party, and an__init__.pywould change the source copied into the Modal images. A per-file ignore changes neither.BackendUnavailableError,BackendTimeoutErrorandTemporaryStage12UnsupportedRequestError, and the executor'sParityMismatchError. Each is caught by name and never logged or stored by class name.TemporaryStage12DispatchFailed, because the entry app logs its class name aserror_type.gateway_models.pykeep their names.exitimport aliased asapp_exit, and four gateway test names shortened for line length.exitobject projects import, and two private accesses with no public equivalent.response_modelarguments that repeat the return type are dropped. The gateway golden OpenAPI is unchanged, and the entry, executor and gateway OpenAPI dumps are identical before and after.printfJSON can't be split without changing the script.src/modal/app.pygains no__future__import. CI's image smoke will run when this PR reaches the bottom of the stack.sim.quality.ruff-lintnow lists the seven families andTID253. The rules tool is synced from the Public API's group 7 (Enable ruff structure and import rules (INP, PLC, SLF, N, A, FAST, E) policyengine-api#3920).Testing
Suites: at this branch's tip after today's restack onto
main, all seven passed:The lazy-import list and the
INP001change touch project code only innoqacomments and configuration. The suites weren't re-run for them.At each tip from Enable ruff structure and import rules (INP, PLC, SLF, N, A, FAST, E) #799 to Register weighted-microdata rules, weight the builder tests, and check change weights and household counts #807:
ruff checkandruff format --check(0.15.22) are clean. From Enable ruff argument, idiom and pytest-style rules (ARG, B, C4, FBT, PERF, PLW, PT, PTH, RUF, SIM and others) #802 up, that includesRUF100, so no suppression is left unused.ci-scriptsguard and the rules tool unittests pass.noqacomments.The lazy-import mechanism was checked with ruff 0.15.22 in a small probe:
PLC0415;TID253flags a module-level import of it;TYPE_CHECKINGis still allowed.Golden OpenAPI: the gateway golden OpenAPI test passes, and the golden file is unchanged.
Integration tests: the conftest change in the integration-test project was checked by parsing only, because its tests need live services.
CI/CD: no workflow changed, and no environment variable was added or rewired.
CI: this is now the lowest unmerged PR in the stack, so its full CI runs.
🤖 Generated with Claude Code