fetch_json with timeout and error mapping
Python
"""Fetch JSON over HTTP with an explicit timeout and readable failures."""
import json
import urllib.error
import urllib.request
from typing import Any
def fetch_json(
url: str, *, timeout: float = 10.0, headers: dict[str, str] | None = None
) -> Any:
"""GET url and decode the body; raise RuntimeError on any failure."""
merged = {"Accept": "application/json", "User-Agent": "bits-fetch/1.0"}
merged.update(headers or {})
request = urllib.request.Request(url, headers=merged)
try:
with urllib.request.urlopen(request, timeout=timeout) as response:
charset = response.headers.get_content_charset() or "utf-8"
body = response.read().decode(charset)
except urllib.error.HTTPError as exc:
raise RuntimeError(f"HTTP {exc.code} for {url}: {exc.reason}") from exc
except urllib.error.URLError as exc:
raise RuntimeError(f"cannot reach {url}: {exc.reason}") from exc
except TimeoutError as exc:
raise RuntimeError(f"timed out after {timeout}s: {url}") from exc
try:
return json.loads(body)
except json.JSONDecodeError as exc:
raise RuntimeError(f"bad JSON from {url}: {exc.msg} at line {exc.lineno}") from exc