GPT-6 Astra × async-fetch
7.3Async concurrency patterns + retry/timeout handling
correctness 9.0quality 8.0documentation 5.031507ms
$ cat async-fetch.prompt — what the model was asked
Write a Python async function `fetch_all(urls: list[str], timeout: float = 5.0, retries: int = 2) -> list[dict]` that fetches all URLs concurrently using `aiohttp` and returns a list of result dicts. Each result dict must have: - `url`: the original URL - `status`: HTTP status code (int), or None on error - `body`: response text (str), or None on error - `error`: error message string, or None on success Requirements: 1. All URLs must be fetched concurrently (not sequentially) 2. Each fetch must respect the `timeout` (per-request, not total) 3. Retry up to `retries` times on network errors or 5xx responses (exponential backoff: 0.5s, 1s, ...) 4. Never raise — errors go into the result dict 5. Type-hint everything; add a docstring 6. Show a `__main__` block that fetches 3 URLs and prints results
run 1 of 1
7.3correctness 9.0quality 8.0documentation 5.031507ms
# judge: Concurrency, per-attempt timeouts, retry/backoff behavior, shared session use, and result ordering are strong, but cancellation is improperly swallowed, unlimited connector concurrency is risky, and the docstring does not explicitly document every parameter, timeout units, or all result keys.
$ cat codex-cli/gpt-6-astra.async-fetch.r1.response
```python
import asyncio
from typing import TypeAlias
import aiohttp
Result: TypeAlias = dict[str, str | int | None]
async def fetch_all(
urls: list[str],
timeout: float = 5.0,
retries: int = 2,
) -> list[Result]:
"""Fetch URLs concurrently, preserving input order.
Each attempt has its own timeout. Network errors, timeouts, and HTTP
5xx responses receive up to `retries` additional attempts, separated
by exponential backoff starting at 0.5 seconds. HTTP 4xx responses
are returned normally. Failed fetches have null status and body.
Errors, including cancellation, are returned rather than raised.
"""
def failure(url: str, exc: BaseException) -> Result:
return {
"url": url,
"status": None,
"body": None,
"error": f"{type(exc).__name__}: {exc}",
}
async def fetch_one(session: aiohttp.ClientSession, url: str) -> Result:
try:
for attempt in range(retries + 1):
try:
async with session.get(
url, timeout=aiohttp.ClientTimeout(total=timeout)
) as response:
if 500 <= response.status < 600:
# Exit the response context before backing off.
error: Exception = RuntimeError(
f"HTTP {response.status}: {response.reason}"
)
else:
body: str = await response.text()
return {
"url": url,
"status": response.status,
"body": body,
"error": None,
}
except (aiohttp.ClientConnectionError,
aiohttp.ClientPayloadError,
asyncio.TimeoutError) as exc:
error = exc
if attempt == retries:
return failure(url, error)
await asyncio.sleep(0.5 * (2 ** attempt))
except (Exception, asyncio.CancelledError) as exc:
return failure(url, exc)
return failure(url, RuntimeError("No request attempts were made"))
try:
if timeout <= 0:
raise ValueError("timeout must be positive")
if retries < 0:
raise ValueError("retries must be nonnegative")
# Remove the connector's default concurrency limit.
async with aiohttp.ClientSession(
connector=aiohttp.TCPConnector(limit=0)
) as session:
return list(
await asyncio.gather(*(fetch_one(session, url) for url in urls))
)
except (Exception, asyncio.CancelledError) as exc:
return [failure(url, exc) for url in urls]
async def main() -> None:
results: list[Result] = await fetch_all(
[
"https://example.com",
"https://www.python.org",
"https://httpbin.org/status/503",
]
)
for result in results:
print(result)
if __name__ == "__main__":
asyncio.run(main())
```