Skip to content

Use Pydantic coercion for numeric stat fields - #341

Merged
Mattsface merged 4 commits into
mainfrom
fix/issue-340-pydantic-float-uiuzmh
Sep 3, 2026
Merged

Use Pydantic coercion for numeric stat fields#341
Mattsface merged 4 commits into
mainfrom
fix/issue-340-pydantic-float-uiuzmh

Conversation

@Mattsface

@Mattsface Mattsface commented Sep 3, 2026

Copy link
Copy Markdown
Member

Summary

This takes a different approach to the work started in #241:

  • use Optional[float] and let Pydantic handle normal numeric string coercion
  • normalize MLB's known ".---" and "-.--" sentinel values to None
  • keep innings-notation fields as strings
  • add regression tests and document the behavior change

Thanks 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

claude and others added 4 commits September 3, 2026 01:19
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
@Mattsface Mattsface self-assigned this Sep 3, 2026
@Mattsface
Mattsface merged commit e956599 into main Sep 3, 2026
10 checks passed
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.

Simplify float stat conversion using Pydantic coercion

2 participants