chore: remove legacy Graphene/Flask (post-cutover cleanup) [WIP]#98
Merged
Conversation
First step of the post-cutover legacy cleanup. handler.py (serverless-wsgi container workaround) and solvis_graphql_api.py (Flask app) are dead — app.py's Mangum handler is the entry. Removed them + the create_app test. Graphene schema + the strawberry delegation stay until the resolver de-graphene (checklist in PR). Suite still green (86 passed).
color_scale/compute.py: graphene-free compute (get_normaliser, log_intervals, get_colour_values, compute_colour_scale -> ColourScaleResult dataclass). strawberry QueryRoot.color_scale now uses it instead of the graphene get_colour_scale. Differential parity green (vs live graphene).
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## deploy-test #98 +/- ##
===============================================
+ Coverage 92.78% 93.82% +1.03%
===============================================
Files 24 12 -12
Lines 1553 1101 -452
Branches 72 64 -8
===============================================
- Hits 1441 1033 -408
+ Misses 88 48 -40
+ Partials 24 20 -4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Build the LocationDetail connection directly from location_by_id + graphql_relay.offset_to_cursor (matches graphene's array cursors) instead of the graphene get_location_detail_list. Differential parity green (incl. Node id + radius_geojson).
Replace the legacy paginated_filtered_ruptures + _to_strawberry_rupture_connection path with graphene-free helpers built directly in strawberry_schema: - _ATTRIBUTE_COLUMN_MAP (was CompositeRuptureDetail.ATTRIBUTE_COLUMN_MAP) - _auto_sorted (port of composite_solution.schema.auto_sorted_dataframe) - _paginated_ruptures (cached.matched_rupture_sections_gdf + graphql_relay cursors) filter_ruptures resolver now builds RuptureDetailConnection itself. Drops the _LegacyFilter/_legacy_filter shims and the composite_solution.schema import. SDL byte-identical; 11/11 differential parity green.
Port the section compute off the legacy graphene CompositeRuptureSections root. The strawberry type now carries the filter input directly (filter_input Private) and resolves graphene-free via new module helpers: - _fault_section_aggregates / _matched_rupture_sections (cached.* wrappers) - _mfd_histogram_rows (build_mfd port) - _fault_surfaces_geojson / _fault_traces_geojson (get_colour_values from compute) - section color_scale via compute_colour_scale with participation-rate fallback Drops the _GrapheneSections/_GrapheneFilterArgs imports and _graphene_sections_root shim; _to_strawberry_color_scale no longer needs the legacy .color_map branch. Adds test_filter_rupture_sections_colour_parity covering the colour paths the plain sections query skips (section color_scale fallback, fault_surfaces/fault_traces + color_scale). SDL byte-identical; 12/12 differential parity green.
…free The strawberry schema imported two pure (pandas/dict) helpers through graphene- importing modules. Move them to graphene-free homes so the schema names no graphene module: - apply_geojson_style -> new geojson_style_util.py (geojson_style.py re-exports it) - rupture_detail -> cached.py (composite_rupture_detail.py re-exports it) strawberry_schema now imports both from the graphene-free modules. The legacy graphene modules keep working via re-export shims until they are deleted at cutover (the package __init__ still eager-loads them, which is removed then). SDL byte-identical; 12/12 differential parity green.
…om tests
The 14 legacy graphene-Client behavioural tests now drive the Strawberry schema via a
graphene.test.Client drop-in (tests/_strawberry_client.py). This keeps their broad coverage
of the migrated resolvers — sorting, filter-set logic, pagination, geojson, node ids — which
the parity corpus alone does not reach. 83 pass, 10 skipped.
test_strawberry_parity converted from an in-process differential (needed the graphene oracle)
to a Strawberry-only snapshot guard; golden outputs captured under tests/__snapshots__ from
the proven-identical schema (regenerate with SNAPSHOT_UPDATE=1).
Fixes one resolver bug the broader suite caught: filter_ruptures passed strawberry.UNSET
through as the sortby 'ascending' value (pandas rejected it); now UNSET fields are omitted so
the legacy '.get("ascending", True)' default applies — matching graphene.
No test imports graphene now. SDL byte-identical; ruff clean.
…e-free
Removes the graphene schema entirely now that the Strawberry schema, the test suite, and the
SDL gate no longer depend on it:
deleted: schema.py (root), location_schema.py, solution_schema.py (dead), geojson_style.py,
color_scale/color_scale.py, composite_solution/{composite_rupture_detail,
composite_rupture_sections,composite_solution,filter_set_logic_options,filtered_ruptures_args,
schema}.py, tools/dump_legacy_sdl.py (the legacy SDL baseline is frozen in schema.legacy.graphql).
Two graphene-free helpers the deleted modules held were relocated to stable homes first:
- auto_sorted_dataframe -> new composite_solution/ruptures.py (also used by the sort unit test)
- _solvis_join -> cached.py (its only consumer)
color compute already lived in color_scale/compute.py.
Gutted composite_solution/__init__.py and color_scale/__init__.py (they eager-loaded the
deleted graphene types). Repointed the two compute unit tests (sort, colour) at the new homes
and dropped the stale get_composite_solution monkeypatches in conftest (everything resolves
through cached now).
No 'import graphene' remains under solvis_graphql_api/. SDL byte-identical; 83 pass, 10 skipped.
With the legacy graphene root schema.py deleted, the canonical name is free. Rename the Strawberry module to solvis_graphql_api/schema.py and repoint importers (app.py, the SDL parity gate, _strawberry_client shim, the snapshot + corpus-replay tests). Module path only — local 'as strawberry_schema' aliases kept. Refreshed the module docstring to point at the graphene-free compute homes (cached, ruptures, color_scale.compute, geojson_style_util). app + handler import OK; SDL byte-identical; 83 pass, 10 skipped; ruff clean.
Remove graphene, graphql-server, flask, flask-cors and serverless-wsgi from project dependencies now that no source imports them. Add graphql-relay as an explicit dep — the schema's relay global-id/cursor encoding used it, but it was only present transitively via graphene. Relock: graphene 3.4.3, flask, flask-cors, graphql-server, serverless-wsgi (and blinker/itsdangerous) all removed from the resolved env; graphql-relay 3.2.0 + graphql-core 3.2.11 (via strawberry) remain. Also narrows the section color_scale normalization default with explicit is-checks (mypy couldn't narrow 'not in (None, UNSET)'). Verified with graphene physically uninstalled: app imports, 83 pass / 10 skipped, SDL byte-identical, mypy + ruff clean.
Add the 2026-06-26 cleanup entry (chunks 1-9: graphene removed from source, tests and deps; test suites repointed at Strawberry; differential parity → snapshot guard) and mark the §5 post-healthy cleanup checklist item.
… (CI cross-platform)
The snapshot conversion (chunk 6) made three parity tests platform-fragile: byte-exact golden
files captured on macOS didn't match ubuntu CI, because shapely buffer coords and matplotlib
hex colours differ in their low float digits. The original differential test was immune (both
schemas ran in one env, so the difference cancelled) — snapshots lost that property.
Keep byte-exact snapshots for the 9 deterministic queries (rounded scalars), and assert the 3
geometry/geojson queries structurally instead: locations_by_id checks the bug-prone global-id
encoding decodes to ('LocationDetail', id) + a non-empty buffer; sections checks counts/extents/
mfd shape + that styling/colour reached every feature. cli_ab_test remains the authoritative
same-data byte-for-byte geojson parity gate (already 9/9).
83 pass / 10 skipped locally; ruff clean.
…apshot) The locations snapshot re-stored the entire nzshm_common LOCATIONS constant — a ~2MB, 118k-line fixture of library data. Assert structurally instead (count == len(LOCATIONS) + a known entry). 12 parity tests pass.
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.
Draft / WIP — the post-cutover legacy cleanup (
MIGRATION_LOG.md§ Cutover plan §5). Per the plan, this is done entirely in test and iterated here; it promotes tomainas one validated PR alongside the migration (your call).Why staged
Deleting Graphene removes two things the new code still uses at runtime/test:
CompositeRuptureSectionsresolvers delegate to the legacy graphene statics (_GrapheneSections);test_strawberry_parity.pydiffs against the grapheneschema_root.So the resolver de-graphene + a snapshot-based parity test must land before the graphene modules can go.
Checklist
handler.py,solvis_graphql_api.py(Flask) + its testcolor_scale.get_colour_scale,location_schema.get_location_detail_list,composite_solution.schema.paginated_filtered_ruptures/build_ruptures_connectionCompositeRuptureSectionsresolvers off_GrapheneSections— callget_fault_section_aggregates+ the mfd/geojson/colour logic directly (keep those compute fns)schema.py(root),solution_schema.py,composite_solution/composite_solution.py; strip fromlocation_schema.py,color_scale/color_scale.py,geojson_style.py,composite_rupture_detail.py,composite_rupture_sections.py,filtered_ruptures_args.py,filter_set_logic_options.pytest_strawberry_parity.pyto snapshot assertions (drop the graphene comparator); delete the 14 graphene-Client(schema_root)test files (corpus replay +cli_ab_testcover the surface); keep the graphene-free fn tests (test_color_scale_fn,test_rupture_sorting_function)strawberry_schema.py→schema.py; fix imports (app.py,tools/schema_parity.py, tests)graphene,graphql-server,flask,flask-cors,serverless-wsgi(python); keepgraphql-relaycli_ab_test9/9