Skip to content

Extract reusable endpoint parsing for sync/async clients #299

Description

@Mattsface

Parent: #297
Depends on: #298

Goal

Identify response parsing and model-construction logic currently embedded in synchronous Mlb endpoint methods that should be shared with the async client.

The objective is to avoid maintaining two independent copies of endpoint parsing without rewriting the existing synchronous API.

Scope

  • Audit representative Mlb methods for transport/parsing coupling
  • Identify minimal shared helpers that can be reused by Mlb and AsyncMlb
  • Extract only logic needed to support the initial async vertical slice
  • Preserve existing return types and endpoint-specific empty-result behavior
  • Keep public method signatures unchanged

Parser package architecture

Create a private, transport-independent parser package under mlbstatsapi:

mlbstatsapi/
├── _parsers/
│   ├── __init__.py
│   ├── teams.py
│   ├── people.py
│   └── schedules.py

The leading underscore marks _parsers as an internal implementation package rather than part of the stable public API.

Organize parsers by domain rather than by individual endpoint. For example, teams.py can eventually contain related team response parsers such as parse_team, parse_teams, roster parsing, and coach parsing as async endpoint coverage expands.

For the initial vertical slice, implement only the parsing/model-construction helpers needed by:

  • get_team
  • get_person
  • get_schedule

The parser package is expected to grow over time as more Mlb endpoints gain async equivalents, so use a package from the beginning rather than a single _parsers.py module.

Responsibility boundary

The parser layer should own only transport-independent response interpretation and model construction.

Mlb / AsyncMlb
    endpoint arguments and endpoint semantics
        ↓
MlbDataAdapter / AsyncMlbDataAdapter
    HTTP, status handling, retries, timeouts, JSON decoding
        ↓
_parsers
    decoded response data -> existing Pydantic/domain models
        ↓
models
    domain object definitions

Keep request construction and endpoint argument validation in Mlb / AsyncMlb.

Keep HTTP status handling, retries, timeouts, cancellation, JSON decoding, and client ownership in the adapters.

Do not make models responsible for understanding endpoint response envelopes such as data["teams"] or data["people"].

Contract requirements from #298

Shared parsing/model code must preserve the behavioral contract defined in #298:

  • Equivalent sync and async endpoint responses produce the same existing Pydantic/domain model types
  • Existing None, [], and {} endpoint behavior remains unchanged
  • Endpoint-specific 404 interpretation remains a public endpoint concern and must not be accidentally changed by parsing extraction
  • JSON-decoded data should enter the same parsing/model-construction path wherever practical
  • Parsing/model failures are not transport failures and must not be retried by shared parsing code
  • Shared helpers must remain transport-independent and must not depend on requests or the selected async HTTP library
  • Do not introduce shared mutable per-request parsing state that could allow concurrent async operations to interfere with one another

Initial parser direction

Prefer small, domain-specific pure functions rather than prematurely introducing a generic parser abstraction.

Conceptually:

parse_team(data) -> Team | None
parse_person(data) -> Person | None
parse_schedule(data) -> Schedule | None

The implementation should preserve existing synchronous behavior first. Once the sync path uses the shared helpers and remains green, the async client can reuse the same parsing functions.

Do not generalize functions such as parse_first_model(data, key, model) until repeated implementations demonstrate a real common abstraction.

Constraints

  • No broad rewrite of mlb_api.py
  • No behavior change to existing synchronous methods
  • No new public API unless explicitly justified
  • Favor small pure parsing/model-construction helpers
  • Do not move HTTP status, retry, timeout, cancellation, or client-lifecycle policy into parsing helpers
  • Do not make _parsers depend on synchronous or asynchronous transport libraries
  • Keep _parsers private; do not re-export parser helpers from the package root

Acceptance criteria

  • Reusable parsing seam identified
  • Private _parsers/ package created with domain-oriented modules for the initial vertical slice
  • Initial shared helpers implemented and tested
  • Existing synchronous tests remain green
  • No changes required for existing Mlb callers
  • Shared logic is sufficient for get_team, get_person, and get_schedule
  • Equivalent parsed input produces the same domain results for sync and async callers
  • Extracted helpers remain transport-independent and safe for concurrent async use
  • Parser helpers are not exposed as part of the stable public API

Refs #297
Contract: #298

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions