mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
retry
This commit is contained in:
parent
27c9752949
commit
c347865b9e
3 changed files with 34 additions and 3 deletions
|
|
@ -11,6 +11,7 @@ def call_with_retry(
|
|||
max_retries: int = 5,
|
||||
backoff_base: float = 1.0,
|
||||
backoff_multiplier: float = 2.0,
|
||||
max_backoff: float = 60.0,
|
||||
) -> T:
|
||||
last_exc: EpcRateLimitError | None = None
|
||||
for attempt in range(max_retries + 1):
|
||||
|
|
@ -19,5 +20,9 @@ def call_with_retry(
|
|||
except EpcRateLimitError as exc:
|
||||
last_exc = exc
|
||||
if attempt < max_retries:
|
||||
time.sleep(backoff_base * (backoff_multiplier ** attempt))
|
||||
if exc.retry_after is not None:
|
||||
delay = exc.retry_after
|
||||
else:
|
||||
delay = backoff_base * (backoff_multiplier ** attempt)
|
||||
time.sleep(min(delay, max_backoff))
|
||||
raise last_exc # type: ignore[misc]
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from datatypes.epc.search import EpcSearchResult
|
|||
|
||||
class EpcClientService:
|
||||
BASE_URL = "https://api.get-energy-performance-data.communities.gov.uk"
|
||||
REQUEST_TIMEOUT = 10.0
|
||||
|
||||
def __init__(self, auth_token: str) -> None:
|
||||
self._headers = {
|
||||
|
|
@ -25,6 +26,16 @@ class EpcClientService:
|
|||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _parse_retry_after(resp: httpx.Response) -> Optional[float]:
|
||||
header = resp.headers.get("Retry-After")
|
||||
if header is None:
|
||||
return None
|
||||
try:
|
||||
return float(header)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
def get_by_certificate_number(self, cert_num: str) -> EpcPropertyData:
|
||||
raw = call_with_retry(lambda: self._fetch_certificate(cert_num))
|
||||
return EpcPropertyDataMapper.from_api_response(raw)
|
||||
|
|
@ -48,11 +59,15 @@ class EpcClientService:
|
|||
f"{self.BASE_URL}/api/certificate",
|
||||
params={"certificate_number": cert_num},
|
||||
headers=self._headers,
|
||||
timeout=self.REQUEST_TIMEOUT,
|
||||
)
|
||||
if resp.status_code == 404:
|
||||
raise EpcNotFoundError(cert_num)
|
||||
if resp.status_code == 429:
|
||||
raise EpcRateLimitError("Rate limited by EPC API")
|
||||
raise EpcRateLimitError(
|
||||
"Rate limited by EPC API",
|
||||
retry_after=self._parse_retry_after(resp),
|
||||
)
|
||||
if not resp.is_success:
|
||||
raise EpcApiError(f"EPC API error {resp.status_code}: {resp.text}")
|
||||
return resp.json()["data"]
|
||||
|
|
@ -72,11 +87,15 @@ class EpcClientService:
|
|||
f"{self.BASE_URL}/api/domestic/search",
|
||||
params=params,
|
||||
headers=self._headers,
|
||||
timeout=self.REQUEST_TIMEOUT,
|
||||
)
|
||||
if resp.status_code == 404:
|
||||
return []
|
||||
if resp.status_code == 429:
|
||||
raise EpcRateLimitError("Rate limited by EPC API")
|
||||
raise EpcRateLimitError(
|
||||
"Rate limited by EPC API",
|
||||
retry_after=self._parse_retry_after(resp),
|
||||
)
|
||||
if not resp.is_success:
|
||||
raise EpcApiError(f"EPC API error {resp.status_code}: {resp.text}")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
from typing import Optional
|
||||
|
||||
|
||||
class EpcApiError(Exception):
|
||||
"""Base for all EPC client errors."""
|
||||
|
||||
|
|
@ -8,3 +11,7 @@ class EpcNotFoundError(EpcApiError):
|
|||
|
||||
class EpcRateLimitError(EpcApiError):
|
||||
"""Raised when the API returns 429 and all retries are exhausted."""
|
||||
|
||||
def __init__(self, message: str, retry_after: Optional[float] = None) -> None:
|
||||
super().__init__(message)
|
||||
self.retry_after = retry_after
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue