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
Refs #297
Contract: #298
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
AsyncMlbDataAdapterwith:aclose()cleanupstrict_http=Falsecompatibility behaviorContract requirements from #298
Lifecycle and ownership
aclose()aclose()is idempotentTimeout and transport failures
timeoutpublic shape, including scalar values and(connect, read)tuplesDEFAULT_TIMEOUT = (3.05, 30.0)MlbTimeoutErrorMlbTransportErrorHTTP behavior
After retries are exhausted:
MlbHttpErrorcontext where available: status, reason, URL, method, and bounded best-effort response contextMlbHttpErrorJSON behavior
{}throughMlbResultMlbDecodeErrorRetry behavior
For library-owned clients:
429,500,502,503,5040.5backoff policyRetry-AfterCaller-injected clients retain caller-controlled retry configuration.
User-Agent
python-mlb-statsapi/<installed-version>with the existingunknownfallbackConcurrency
Compatibility
Equivalent responses should map to the same public
MlbResultand exception hierarchy used by the synchronous transport wherever practical.Caller-owned clients must not be closed or unexpectedly reconfigured by the library.
Acceptance criteria
aclose()is idempotentRefs #297
Contract: #298