Skip to content

Implement AsyncMlbDataAdapter #301

Description

@Mattsface

Parent: #297
Depends on: #298, #300

Goal

Implement the asynchronous transport layer for MLB Stats API requests while preserving the behavioral contract established for the synchronous adapter.

Scope

Implement AsyncMlbDataAdapter with:

  • Async GET requests
  • Library-owned async client creation
  • Caller-injected async client support
  • aclose() cleanup
  • Ownership tracking
  • Timeout handling
  • Transport error mapping
  • HTTP error mapping
  • JSON decode errors
  • Strict HTTP default behavior
  • Explicit strict_http=False compatibility behavior
  • Endpoint-specific 404 behavior
  • Retry behavior defined by Define the v1.1 async API and transport contract #298
  • User-Agent behavior consistent with the installed package version
  • Cancellation behavior defined by Define the v1.1 async API and transport contract #298
  • Same-event-loop concurrent request support

Contract requirements from #298

Lifecycle and ownership

  • Library-created async clients are library-owned, configured by the library, and closed by aclose()
  • Caller-injected clients are caller-owned and must not be closed, silently reconfigured, or mutated by the library
  • aclose() is idempotent
  • Cleanup must not suppress or replace the original exception or caller cancellation

Timeout and transport failures

  • Preserve the sync timeout public shape, including scalar values and (connect, read) tuples
  • Preserve DEFAULT_TIMEOUT = (3.05, 30.0)
  • Translate timeout configuration internally without mutating injected clients
  • Async timeout failures raise MlbTimeoutError
  • Other async request/transport failures raise MlbTransportError
  • Underlying HTTP-library exceptions must remain private implementation details

HTTP behavior

After retries are exhausted:

2xx                         -> normal decode / MlbResult
404                         -> empty MlbResult
non-404 4xx + strict=True   -> MlbHttpError
non-404 4xx + strict=False  -> MlbHttpCompatibilityWarning + empty MlbResult
5xx                         -> MlbHttpError
other non-2xx               -> MlbHttpError
timeout                     -> MlbTimeoutError
transport failure           -> MlbTransportError
caller cancellation         -> asyncio.CancelledError
  • Preserve structured MlbHttpError context where available: status, reason, URL, method, and bounded best-effort response context
  • Error-context extraction failures must never replace the original MlbHttpError
  • Compatibility warnings must not leak bodies, headers, credentials, or other sensitive context

JSON behavior

  • Empty successful response bodies produce {} through MlbResult
  • Valid JSON enters the shared parsing/model path
  • Invalid JSON on a successful response raises MlbDecodeError
  • Decode failures are not retried

Retry behavior

For library-owned clients:

  • Retry only GET requests
  • Preserve retryable statuses 429, 500, 502, 503, 504
  • Preserve equivalent total/connect/read/status limits and effective 0.5 backoff policy
  • Respect Retry-After
  • Never retry caller-requested cancellation
  • Do not retry JSON decode or model/parsing failures
  • Backoff must not block the event loop

Caller-injected clients retain caller-controlled retry configuration.

User-Agent

  • Library-owned clients use python-mlb-statsapi/<installed-version> with the existing unknown fallback
  • Caller-injected client headers remain caller-controlled

Concurrency

  • A single adapter/client instance must support multiple concurrent in-flight requests on the same event loop
  • Per-request state must remain independent
  • Cancelling one request must not implicitly cancel unrelated concurrent requests
  • Do not create hidden worker pools, unrelated background work, or automatic fan-out
  • Cross-thread and cross-event-loop use are not required in v1.1

Compatibility

Equivalent responses should map to the same public MlbResult and exception hierarchy used by the synchronous transport wherever practical.

Caller-owned clients must not be closed or unexpectedly reconfigured by the library.

Acceptance criteria

  • Async adapter can perform successful GET requests
  • Library-owned async client lifecycle is correct and aclose() is idempotent
  • Injected client ownership and configuration are preserved
  • 404 behavior matches the 1.x contract
  • Strict and compatibility non-404 4xx behavior matches Define the v1.1 async API and transport contract #298
  • Final 5xx and other non-2xx behavior matches Define the v1.1 async API and transport contract #298
  • Timeout/transport/decode failures use the public exception hierarchy
  • Structured HTTP error context matches the public sync contract where available
  • Retry behavior matches Define the v1.1 async API and transport contract #298 and does not block the event loop
  • User-Agent behavior is correct
  • Cancellation is not swallowed, remapped, or retried
  • Multiple concurrent requests can safely share one adapter/client on the same event loop
  • Cancelling one concurrent request does not implicitly cancel an unrelated request

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