52 lines
1.5 KiB
Python
52 lines
1.5 KiB
Python
from __future__ import annotations
|
|
|
|
import httpx
|
|
|
|
|
|
def _runtime_error_detail(exc: Exception) -> str | None:
|
|
if not isinstance(exc, RuntimeError):
|
|
return None
|
|
msg = str(exc).strip()
|
|
return msg or None
|
|
|
|
|
|
def _http_message_from_payload(payload: object) -> str | None:
|
|
if isinstance(payload, dict):
|
|
raw = payload.get("errorMessage") or payload.get("error") or payload.get("message")
|
|
if isinstance(raw, str) and raw.strip():
|
|
return raw.strip()
|
|
if isinstance(payload, str) and payload.strip():
|
|
return payload.strip()
|
|
return None
|
|
|
|
|
|
def _http_response_message(response: httpx.Response) -> str | None:
|
|
try:
|
|
payload = response.json()
|
|
except Exception:
|
|
text = (response.text or "").strip()
|
|
return text or None
|
|
return _http_message_from_payload(payload)
|
|
|
|
|
|
def _http_error_detail(exc: httpx.HTTPStatusError) -> str:
|
|
detail = f"http {exc.response.status_code}"
|
|
msg = _http_response_message(exc.response)
|
|
if not msg:
|
|
return detail
|
|
msg = " ".join(msg.split())
|
|
return f"{detail}: {msg[:200]}"
|
|
|
|
|
|
def safe_error_detail(exc: Exception, fallback: str) -> str:
|
|
"""Return a user-safe error message without leaking noisy exception internals."""
|
|
|
|
runtime_detail = _runtime_error_detail(exc)
|
|
if runtime_detail:
|
|
return runtime_detail
|
|
if isinstance(exc, httpx.HTTPStatusError):
|
|
return _http_error_detail(exc)
|
|
if isinstance(exc, httpx.TimeoutException):
|
|
return "timeout"
|
|
return fallback
|