Use Pydantic coercion for numeric stat fields - #341
Merged
Conversation
Closes #340. Replaces the string-typed averages, percentages, ratios, and rate stats across SimpleCatchingSplit, SimpleFieldingSplit, AdvancedHittingSplit, SimpleHittingSplit, SimplePitchingSplit, AdvancedPitchingSplit, and ExpectedStatistics with Optional[float] (or float for ExpectedStatistics' required fields). Pydantic's native coercion converts MLB's numeric-string payloads (e.g. ".287") without any custom validator, so this avoids the BeforeValidator/OptionalFloat approach proposed in PR #241. No sentinel value (".---", "-", etc.) appears in this repo's fixtures or tests, so unexpected malformed values raise ValidationError instead of being silently coerced to None. MLB innings notation fields keep their str type since "6.2" means 6 2/3 innings, not the decimal 6.2: SimpleFieldingSplit.innings, SimplePitchingSplit.innings_pitched, and AdvancedPitchingSplit.innings_pitched_per_game are unchanged. This is a breaking change for code comparing these fields as strings (e.g. avg == ".287" or avg.startswith(".")); documented in docs/stats.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBCKzuwwj85qDEaDqqxALM
The live MLB Stats API returns ".---" and "-.--" as placeholder values for rate/ratio stats that don't apply (e.g. caught-stealing percentage when no steal attempts occurred). Plain Optional[float] rejects these, breaking several external tests added since #340 converted these fields from str. Adds a shared normalize_mlb_float_sentinel() helper and field_validator (mode="before") declarations on SimpleCatchingSplit, SimpleFieldingSplit, SimpleHittingSplit, AdvancedHittingSplit, SimplePitchingSplit, AdvancedPitchingSplit, and ExpectedStatistics that map only those two known sentinels to None, leaving any other malformed string to raise ValidationError as before. Field annotations stay plain Optional[float]; innings-notation fields remain untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Rf8xvVc7a1TrPZEKaF8vb
normalize_mlb_float_sentinel() used `value in MLB_FLOAT_SENTINELS`, which raises TypeError for unhashable values like dicts or lists before Pydantic gets a chance to validate. Restrict the membership check to strings so unexpected shapes pass through untouched and surface as a normal ValidationError instead of crashing inside the helper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Rf8xvVc7a1TrPZEKaF8vb
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
This takes a different approach to the work started in #241:
Optional[float]and let Pydantic handle normal numeric string coercion".---"and"-.--"sentinel values toNoneThanks to @louisadamian for the original work in #241. That PR identified the affected fields and helped uncover the sentinel-value behavior this builds on.
Supersedes #241
Closes #340