Results, batching and retries
Inspect a result
result = await http.get("/items")if result.ok: payload = result.unwrap().payloadelse: status = result.status error = result.error body = result.response.content if result.response is not None else None| Value | Meaning |
|---|---|
result.completed | Execution, body read and decoding completed |
result.ok | Completed and terminal HTTP status is 2xx |
result.status | Terminal response status, or None when no terminal response arrived |
result.error | Recorded exception, if one exists |
result.attempts | Runner attempts, not redirect/auth exchange count |
result.elapsed | Execution elapsed duration |
result.response | Terminal attempt’s terminal response evidence |
result.history | Followed redirect/auth response heads in the terminal attempt |
result.execution | Existing common-execution report, including retry evidence |
A completed 404 remains inspectable but is not ok. A connection failure has no
fabricated status. Size-limit and read failures retain any collected bytes with
body_complete=False. Decode failures retain complete bytes with decoded_as=None.
JSON null is a successfully decoded None; empty content is separately labelled
decoded_as="empty". A complete 206 response does not imply the complete resource.
unwrap() re-raises recorded exceptions. For unsuccessful outcomes without an
exception, it raises HttpResultError carrying .result. Routine result repr
omits payloads; explicit body/header/error access can reveal sensitive data.
Batch once
async with HttpClient.create(base_url="https://provider.example/", max_concurrency=8) as http: messages = [http.build_request(f"/items/{number}") for number in range(4)] batch = await http.batch(messages) for message, result in zip(messages, batch.results, strict=True): if result.ok: consume(result.unwrap().payload)The batch retains input order, including failures. It uses one execution runner
over the underlying attempt method. It does not run execution-managed send
inside another execution batch. A single call likewise has one runner. Call
configuration applies to the whole batch; use separate calls for different policies.
Concurrency is bounded within a batch, not across independently started calls or
across containers. fail_fast follows execution failures and batch boundaries;
a successfully returned HTTP 404 alone is not an execution failure. Skipped items
retain their positions according to the execution result contract.
Opt into retries
import httpxfrom common_execution import RetryPolicyfrom common_http import HttpClient
policy = RetryPolicy.http( max_retries=2, extra_exceptions=(httpx.ConnectError, httpx.ReadTimeout), delay=0.25, max_delay=5,)async with HttpClient.create(base_url="https://provider.example/", retry=policy) as http: result = await http.get("/items")The default makes no retries. RetryPolicy.http selects 429/500/502/503/504 by
default and accepts a different statuses set. Select native transport exceptions
explicitly. Server Retry-After hints are bounded by the configured maximum delay.
The compatible execution release must include HTTP-date and typed-header support.
The policy authorizes repeating the operation, including writes. Decide whether a write is repeatable and whether its provider supports an idempotency key. An error does not prove the remote server did not act. HTTP does not invent idempotency keys or turn all errors into retries. Cancellation is resource cleanup, not a replay.
Pacing and time budgets
from common_execution import RateLimit
call = http.call_config(rate_limit=RateLimit.per_second(5), timeout=60)The inherited limiter paces task starts within that run. It is not a client-global
or distributed quota, and does not charge every redirect/auth exchange. Supply
admit= for admission before each physical exchange; see
integration boundaries.
timeout is per attempt. Network timeouts apply to phases/chunk waits. For a whole
logical operation deadline including retry waits:
import asyncio
async with asyncio.timeout(120): result = await http.get("/items")Close a client only after its operations finish or are cancelled and awaited. Active-operation checks include retry delays and complete batches.