Skip to content

Split the simulation runtime and its output builder tests under the file-length limit - #942

Merged
anth-volk merged 1 commit into
mainfrom
file-length-split-simulation-runtime
Oct 27, 2026
Merged

anth-volk merged 1 commit into
mainfrom
file-length-split-simulation-runtime

Conversation

@anth-volk

Copy link
Copy Markdown
Contributor

Fixes #941

Same-Repository Draft PR Check

  • This PR is a draft.
  • This PR is from a branch in PolicyEngine/policyengine-sim-api, not a personal fork.

Summary

Seventh of nine stacked PRs; it builds on #938. It splits the executor's simulation_runtime.py and its output builder tests without changing behaviour, and removes both from the file-length baseline (count 4 to 2). Scripts compared every top-level statement of each old file with the new files: all appear exactly once and unchanged apart from the renames below and one logger line, in their original order.

simulation_runtime.py (1,111 lines)

Module Lines Holds
gcp_credentials.py 119 writing the worker's Google Cloud credentials
request_policy.py 133 the request's year and its policy periods
region_resolution.py 283 RegionResolution, resolving a request's region or region group, and checking a local-area breakdown request
simulation_runtime.py 651 dataset selection and loading, simulation building, local-area breakdowns, the run, and re-exports
  • Why simulation_runtime.py stays at 651 lines, above the 400 the split aims for. Tests across the executor (the runtime, precompute, Stage 12, job-progress and segmented-national tests) replace _country_module, _load_dataset, _build_simulation, _resolve_dataset_selection, resolve_data_folder, SimulationOutputBuilder and others by name on simulation_runtime. A replacement only reaches code that looks the name up in that module, and _load_dataset and _build_simulation themselves call _country_module and resolve_data_folder, so moving any of these apart would silently stop some replacements applying. They stay together; the code moved out reads none of them.
  • Logging: gcp_credentials.py names the policyengine_simulation_executor.simulation_runtime logger explicitly, so the credential messages keep their logger; this is the one statement that isn't verbatim.
  • Import order: the moved modules import nothing the old module didn't, so POLICYENGINE_SKIP_COUNTRY_IMPORTS is still set before any policyengine import.

Renames

Private names another module now uses are public, each with a docstring where it had none: _parse_year to parse_year, _normalise_policy to normalise_policy, _compile_interval_policy to compile_interval_policy, _runtime_policy to wrapper_policy (its docstring says it converts a request's policy into the form the wrapper receives; _build_simulation already has a local variable named runtime_policy), _resolve_region to resolve_region, _resolve_dataset_reference to resolve_dataset_reference, and _require_local_area_breakdown to require_local_area_breakdown.

simulation_runtime.py re-exports them. The modules that imported the private names from it now import the public names from it, so tests that replace them on simulation_runtime still reach those modules: src/modal/segmented_national.py, spm.py, precompute_planning.py, stage12_cache/precompute_planning.py, stage12_runtime/aggregation.py, stage12_runtime/simulation.py and stage12_worker_validation.py.

Tests: tests/test_simulation_output_builder.py (3,048 lines)

File Lines
test_simulation_output_fakes.py (shared fakes, no tests) 290
test_simulation_output_builder.py 444
test_simulation_output_impacts.py 338
test_simulation_output_runtime_runs.py 419
test_simulation_output_dataset_loading.py 242
test_simulation_output_dataset_selection.py 354
test_simulation_output_uk_local_areas.py 802
test_simulation_output_region_resolution.py 257
  • Every file is named test_simulation_output_*.py, the pattern sim.microdata.builder-tests-use-weighted-frames checks, so the entity frames these tests and fakes build are still checked; the fakes module's docstring says so. test_simulation_output_uk_local_areas.py keeps the routed-UK tests together with the fakes only they use.
  • The shared fakes are public: _FakeOutputDataset, _FakeSimulation, _TrackedSpan, _TrackingRuntime, _with_observability_timings, _macro_baseline_reform, _congressional_district_output, _constituency_impact_record, _local_authority_impact_record, _stub_policyengine_output_calls and _build_schema_output drop their underscore, and _simulation_output_builder is make_output_builder.
  • All 1,895 executor tests are collected before and after, with the same names.
  • Patch targets (a script listed, for each replacement, the modules whose code reads the name):
    • the region-group tests (tests/test_region_group_resolution.py) and the SPM boundary helper (tests/test_spm_runtime_native.py) replace resolve_dataset_reference in region_resolution, where regions are resolved;
    • the identity fixture in tests/test_precompute_planning.py replaces get_country_release_bundle in both simulation_runtime and region_resolution, which both read it, as the one replacement did before;
    • every other replacement still targets simulation_runtime, under the new name where the helper was renamed (resolve_region in seven tests and helpers);
    • tests that import or read a private helper that moved now take it from its new module: _normalize_credentials_blob (gcp_credentials), _parse_policy_period (request_policy), _normalise_region_code and _resolve_region_group (region_resolution).

Environment variables

No change: setup_gcp_credentials reads the same GOOGLE_APPLICATION_CREDENTIALS_JSON and GOOGLE_APPLICATION_CREDENTIALS variables in its new module, and no workflow, image or deployment file changes.

Testing

  • Executor, full suite: 1,873 passed, 22 skipped, as before; the weighted-frame guard finds nothing in the twelve files it now scans.
  • Entrypoint: 742 passed. Gateway: 268 passed.
  • Pyright for the executor: no errors.
  • Ruff 0.15.22 format and lint: clean.
  • python scripts/run_quality_guards.py and python -m scripts.rules guard --base-ref origin/file-length-split-precompute: passed; python -m scripts.rules render leaves no diff.

Only the lowest PR in the stack runs full CI, so this PR's CI complete check fails by design until the PRs below it merge.

🤖 Generated with Claude Code

@anth-volk
anth-volk added this pull request to stack #928 October 27, 2026 13:55
@anth-volk
anth-volk force-pushed the file-length-split-simulation-runtime branch 4 times, most recently from 75099c3 to 248b72f Compare October 27, 2026 16:52
Base automatically changed from file-length-split-precompute to main October 27, 2026 17:00
simulation_runtime.py (1,111 lines) and
tests/test_simulation_output_builder.py (3,048 lines) were over the
file-length limits. The code moves unchanged.

Three parts of simulation_runtime.py move out:

- gcp_credentials.py: writing the worker's Google Cloud credentials; its
  messages still log under the simulation_runtime logger, now named
  explicitly
- request_policy.py: the request's year and its policy periods
- region_resolution.py: resolving a request's region, and checking a
  local-area breakdown request

simulation_runtime.py keeps dataset selection and loading, simulation
building, local-area breakdowns and the run itself (651 lines). Tests in
several files replace those functions by name on simulation_runtime, and
the code that calls them has to look them up there for the replacements
to apply, so they stay together.

Private names that another module now uses are public, with a docstring
where they had none: _parse_year, _normalise_policy,
_compile_interval_policy, _runtime_policy (as wrapper_policy, since
_build_simulation has a local variable named runtime_policy),
_resolve_region, _resolve_dataset_reference and
_require_local_area_breakdown. simulation_runtime.py re-exports them, and
the modules that imported the private names from it (segmented_national,
spm, both precompute planners, Stage 12 aggregation and simulation, and
worker validation) import the public names from it instead.

The tests move to seven files and a module of shared fakes, all named
tests/test_simulation_output_*.py, the files the weighted-frame rule
(sim.microdata.builder-tests-use-weighted-frames) checks, so the entity
frames they build stay checked. The shared fakes are now public. All
1,895 executor tests keep their names. Patches follow the code: the
region group and SPM boundary tests patch resolve_dataset_reference in
region_resolution, where regions are resolved; the identity fixture
replaces get_country_release_bundle in both simulation_runtime and
region_resolution, which both read it; and tests that patched or read a
renamed helper use its new name, or the module a private helper now
lives in.

The file-length baseline drops both files (count 4 to 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anth-volk
anth-volk force-pushed the file-length-split-simulation-runtime branch from 248b72f to 382e5dc Compare October 27, 2026 17:00
@anth-volk
anth-volk marked this pull request as ready for review October 27, 2026 17:10
@anth-volk
anth-volk requested a review from a team as a code owner October 27, 2026 17:10
@anth-volk
anth-volk merged commit c90abf0 into main Oct 27, 2026
23 checks passed
@anth-volk
anth-volk deleted the file-length-split-simulation-runtime branch October 27, 2026 17:11
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.

Split the simulation runtime and its output builder tests under the file-length limit

1 participant