"""Minimal HTTP client over urllib: timeout, small retry, user-agent. Injectable.""" from __future__ import annotations import json as _json import time import urllib.request USER_AGENT = "human-experience-filter-ingest/0.1 (+local art installation)" class HttpClient: def __init__(self, *, timeout=30.0, retries=2, opener=None): self.timeout, self.retries = timeout, retries self._opener = opener or urllib.request.urlopen def get_bytes(self, url, *, headers=None): last = None for attempt in range(self.retries + 1): try: req = urllib.request.Request( url, headers={"User-Agent": USER_AGENT, **(headers or {})} ) with self._opener(req, timeout=self.timeout) as resp: return resp.read() except Exception as exc: # noqa: BLE001 - retried below last = exc if attempt < self.retries: time.sleep(0.2 * (attempt + 1)) raise last def get_json(self, url, *, headers=None): return _json.loads(self.get_bytes(url, headers=headers).decode("utf-8"))