Align horizon_type Literals with server enum (add quarter)#6
Merged
Conversation
Several client methods annotated horizon_type as an inline
Literal[...] that had drifted out of sync: most omitted "quarter"
even though the server's HorizonType enum accepts it (day, pentad,
decade, month, quarter, season, year). A caller passing
horizon_type="quarter" through those methods got a confusing
client-side type/validation error for a value the server accepts
(SAPPHIRE INFRA-019).
Introduce a single shared HorizonTypeLiteral alias in validators.py
as the one place the value set is declared, and derive the runtime
VALID_HORIZONS set from it via typing.get_args so the type hint and
the validation allowlist cannot drift again. Replace every inline
horizon_type Literal with the shared alias and export it from the
package.
The narrower VALID_LONG_FORECAST_HORIZONS set ({month, quarter,
season}) is a deliberately separate concept and is left untouched;
long_term horizon_type params are str (not Literal) and out of scope.
This only widens what the type hints accept; runtime behavior for
existing values is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <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.
Summary
Several client methods annotated
horizon_typeas an inlineLiteral[...]that had drifted out of sync with each other and with the server. Most omitted"quarter"even though the server'sHorizonTypeenum accepts exactlyday, pentad, decade, month, quarter, season, year. A caller passinghorizon_type="quarter"through one of those methods got a confusing client-side type/validation error for a value the server accepts (SAPPHIRE INFRA-019).This PR makes every
horizon_typeLiteral equal to the full server value set and refactors so they cannot drift again.Single source of truth
validators.pynow declares the value set once:The Literal alias drives the static type hints; the runtime validation set is derived from it.
HorizonTypeLiteralis exported from the package.Changed
horizon_typedeclarationsvalidators.py:14,19HorizonTypeLiteral+ derivedVALID_HORIZONSVALID_HORIZONSwas a hand-written setget_argspreprocessing.py:109prepare_runoff_recordsLiteral["day","pentad","decade","month","season","year"](no quarter)HorizonTypeLiteralpreprocessing.py:214prepare_hydrograph_recordsLiteral[...](no quarter)HorizonTypeLiteralshort_term.py:114prepare_short_term_forecast_recordsLiteral[...](no quarter)HorizonTypeLiteralpostprocessing.py:95prepare_short_term_forecast_records(facade)Literal[...](no quarter)HorizonTypeLiteralpostprocessing_base.py:100prepare_skill_metric_recordsLiteral[...](already had quarter)HorizonTypeLiteral(now shared)Also: removed the now-unused
Literalimport fromshort_term.py,postprocessing.py,postprocessing_base.py; updated twopreprocessing.pydocstrings to listquarter; exportedHorizonTypeLiteralin__init__.py.The shared
HorizonTypeLiteralalias invalidators.pyis the only place the 7-value set is declared.Separate horizon set — found and intentionally left alone
VALID_LONG_FORECAST_HORIZONS = {"month", "quarter", "season"}is a deliberately narrower set for long-term forecasts. It is not ahorizon_typeLiteral annotation (thelong_term.pyhorizon_typeparams are plainstr, validated at runtime against this narrower set), so it is out of scope and was not widened.Backward compatibility
This change only widens what the type hints accept. Runtime behavior for existing values, call sites, and signatures is unchanged.
Tests
test_validators.py: assertHorizonTypeLiteralequals the full server enum and thatVALID_HORIZONS == set(get_args(HorizonTypeLiteral))(single-source-of-truth check).test_preprocessing.py/test_short_term.py: parametrized tests assertingprepare_runoff_records,prepare_hydrograph_records, andprepare_short_term_forecast_recordsaccept all seven horizons (incl.quarter)."fortnight") on theread_*methods.uv run pytest→ 216 passed.uv run mypy→ clean.Version
Bumped
0.4.0→0.5.0(pyproject +__init__), CHANGELOG updated. Downstream pins by git rev/version, so this is a clean new rev for the INFRA-019 re-pin.🤖 Generated with Claude Code