Llama 3.3 70B × async-fetch
5.7Async concurrency patterns + retry/timeout handling
correctness 4.0quality 6.0documentation 7.01760ms
$ 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
5.7correctness 4.0quality 6.0documentation 7.01760ms
# judge: Critical bug: timeouts raise asyncio.TimeoutError which is not caught by the except aiohttp.ClientError clause, and gather() lacks return_exceptions=True, so the function can still raise on timeout, violating the 'never raise' requirement; also timeout is passed as a raw float rather than an explicit aiohttp.ClientTimeout object as the rubric expects, though backoff and session reuse are implemented correctly.
$ cat llama-3.3-70b.async-fetch.r1.response
```python
import aiohttp
import asyncio
from typing import List, Dict
async def fetch_url(session: aiohttp.ClientSession, url: str, timeout: float, retries: int) -> Dict:
"""Fetch a single URL with retries and timeout."""
for attempt in range(retries + 1):
try:
async with session.get(url, timeout=timeout) as response:
status = response.status
body = await response.text()
if 500 <= status < 600: # 5xx response
if attempt < retries:
await asyncio.sleep(2 ** attempt * 0.5) # exponential backoff
continue
return {
'url': url,
'status': status,
'body': body,
'error': None,
}
except aiohttp.ClientError as e:
if attempt < retries:
await asyncio.sleep(2 ** attempt * 0.5) # exponential backoff
continue
return {
'url': url,
'status': None,
'body': None,
'error': str(e),
}
# This should not happen, as we already handled all attempts
return {
'url': url,
'status': None,
'body': None,
'error': 'Failed after all retries',
}
async def fetch_all(urls: List[str], timeout: float = 5.0, retries: int = 2) -> List[Dict]:
"""
Fetch all URLs concurrently using aiohttp and return a list of result dicts.
Args:
- urls: List of URLs to fetch
- timeout: Per-request timeout (default: 5.0 seconds)
- retries: Number of retries on network errors or 5xx responses (default: 2)
Returns:
- A list of dicts, each containing:
- '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
"""
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url, timeout, retries) for url in urls]
return await asyncio.gather(*tasks)
if __name__ == '__main__':
async def main():
urls = ['http://example.com', 'http://example.org', 'http://example.net']
results = await fetch_all(urls)
for result in results:
print(f"URL: {result['url']}, Status: {result['status']}, Error: {result['error']}")
if result['error'] is None:
print(f"Body: {result['body'][:50]}...")
asyncio.run(main())
```
In this code, the `fetch_url` function fetches a single URL with retries and timeout. It returns a dict containing the URL, status, body, and error (if any). The `fetch_all` function creates a list of tasks to fetch all URLs concurrently and uses `asyncio.gather` to wait for all tasks to complete.
The `__main__` block demonstrates how to use the `fetch_all` function to fetch 3 URLs and print the results.