|
|
|
import os, httpx, asyncio |
|
from functools import lru_cache |
|
from typing import Any, Dict |
|
|
|
class APIError(Exception): pass |
|
|
|
class BaseClient: |
|
def __init__(self, base: str, api_key_env: str = None): |
|
self.base = base |
|
self.key = os.getenv(api_key_env) if api_key_env else None |
|
self._client = httpx.AsyncClient(timeout=10) |
|
|
|
async def request(self, method: str, path: str, **kwargs) -> Any: |
|
|
|
if self.key: |
|
if "headers" not in kwargs: kwargs["headers"] = {} |
|
kwargs["headers"]["Authorization"] = f"Bearer {self.key}" |
|
url = f"{self.base.rstrip('/')}/{path.lstrip('/')}" |
|
resp = await self._client.request(method, url, **kwargs) |
|
try: |
|
resp.raise_for_status() |
|
except httpx.HTTPStatusError as e: |
|
raise APIError(f"{resp.status_code} @ {url}") from e |
|
|
|
ct = resp.headers.get("Content-Type","") |
|
return resp.json() if ct.startswith("application/json") else resp.text |
|
|
|
async def close(self): |
|
await self._client.aclose() |
|
|