Source code for pyfetcher.contracts.response
"""Response models for :mod:`pyfetcher`.
Purpose:
Provide normalized response types independent of the underlying transport.
Design:
- Response objects are transport-agnostic.
- Streaming chunks are modeled separately from full responses.
- Batch responses preserve ordering and capture success/failure per request.
Examples:
::
>>> response = FetchResponse(
... request_url="https://example.com/",
... final_url="https://example.com/",
... status_code=200,
... headers={},
... backend="httpx",
... elapsed_ms=10.0,
... )
>>> response.ok
True
"""
from __future__ import annotations
from pydantic import BaseModel, ConfigDict, computed_field
from pyfetcher.contracts.request import BackendKind
[docs]
class FetchResponse(BaseModel):
"""Normalized fetch response.
A transport-agnostic response model that captures the HTTP status,
headers, body content, and timing information for a completed request.
The :attr:`ok` computed property provides a quick success check.
Args:
request_url: Original request URL as a string.
final_url: Final URL after any redirects.
status_code: HTTP status code.
headers: Response headers as a flat dict.
content_type: Response ``Content-Type`` header value, if present.
text: Decoded text body when fully loaded.
body: Raw bytes body when available.
backend: Name of the backend that executed the request.
elapsed_ms: Total elapsed time in milliseconds.
Examples:
::
>>> response = FetchResponse(
... request_url="https://example.com/",
... final_url="https://example.com/",
... status_code=204,
... headers={},
... backend="httpx",
... elapsed_ms=1.0,
... )
>>> response.ok
True
"""
model_config = ConfigDict(extra="forbid", frozen=True)
request_url: str
final_url: str
status_code: int
headers: dict[str, str]
content_type: str | None = None
text: str | None = None
body: bytes | None = None
backend: BackendKind
elapsed_ms: float
@computed_field
@property
def ok(self) -> bool:
"""Return whether the response indicates success.
Returns:
``True`` for 2xx and 3xx status codes.
Examples:
::
>>> FetchResponse(
... request_url="https://example.com/",
... final_url="https://example.com/",
... status_code=200,
... headers={},
... backend="httpx",
... elapsed_ms=1.0,
... ).ok
True
"""
return 200 <= self.status_code < 400
[docs]
class StreamChunk(BaseModel):
"""Single streamed response chunk.
Represents one chunk of a streaming HTTP response, carrying the raw
bytes along with positional metadata for ordered reassembly.
Args:
request_url: Original request URL.
final_url: Final URL after redirects.
backend: Backend that produced this chunk.
index: Zero-based chunk index within the stream.
data: Raw bytes payload for this chunk.
Examples:
::
>>> StreamChunk(
... request_url="https://example.com/",
... final_url="https://example.com/",
... backend="aiohttp",
... index=0,
... data=b"abc",
... ).index
0
"""
model_config = ConfigDict(extra="forbid", frozen=True)
request_url: str
final_url: str
backend: BackendKind
index: int
data: bytes
[docs]
class BatchItemResponse(BaseModel):
"""Result of a single batch item.
Captures either a successful :class:`FetchResponse` or an error message
for one request within a batch execution.
Args:
request_url: Original request URL.
ok: Whether the item succeeded.
response: The fetch response on success.
error: Error message string on failure.
Examples:
::
>>> item = BatchItemResponse(
... request_url="https://example.com/",
... ok=False,
... error="boom",
... )
>>> item.ok
False
"""
model_config = ConfigDict(extra="forbid", frozen=True)
request_url: str
ok: bool
response: FetchResponse | None = None
error: str | None = None
[docs]
class BatchFetchResponse(BaseModel):
"""Response container for batch fetch execution.
Wraps the results of a :class:`~pyfetcher.contracts.request.BatchFetchRequest`,
preserving input order so callers can correlate responses to their
original requests by index.
Args:
items: Batch item responses in input order.
Examples:
::
>>> BatchFetchResponse(items=[]).items
[]
"""
model_config = ConfigDict(extra="forbid", frozen=True)
items: list[BatchItemResponse]