Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import os
- from collections.abc import Mapping
- from types import TracebackType
- from typing import Any, Self
- import httpx
- from loguru import logger
- def _get_api_key_env(key_name: str) -> str:
- try:
- return os.environ[key_name]
- except KeyError as e:
- raise MissingKeyError(key_name=key_name) from e
- class APIClient:
- def __init__(
- self,
- *,
- http_client: httpx.AsyncClient | None = None,
- api_key: str | None = None,
- base_url: str = "https://api.example.com/",
- retries: int = 3,
- timeout: float = 10.0,
- **kwargs: Any,
- ) -> None:
- if http_client is not None:
- self._session = http_client
- else:
- self.api_key = api_key or _get_api_key_env("API_KEY") # api key loading under else
- transport = httpx.AsyncHTTPTransport(retries=retries)
- session_args = dict(
- base_url=base_url,
- transport=transport,
- timeout=timeout,
- params={"key": self.api_key},
- )
- self._session = httpx.AsyncClient(**session_args, **kwargs)
- async def _request(
- self,
- method: str,
- path: str,
- params: Mapping[str, Any] | None = None,
- data: Mapping[str, Any] | None = None,
- ) -> str:
- try:
- response = await self._session.request(
- method=method, url=path, params=params, json=data
- )
- response.raise_for_status()
- except httpx.RequestError as e:
- logger.error("Problem with API request: {}", str(e))
- raise RequestError(str(e)) from e
- except httpx.HTTPStatusError as e:
- res_body = e.response.json()
- err_msg = res_body["error"]["message"]
- logger.error("Failed to fetch API data: {}", err_msg)
- raise ResponseError(
- status_code=e.response.status_code, message=err_msg
- ) from e
- return response.text
- async def close(self) -> None:
- """Closes the client."""
- await self._session.aclose()
- async def __aenter__(self) -> Self:
- """Opens the client."""
- return self
- async def __aexit__(
- self,
- exc_type: type[BaseException] | None,
- exc_val: BaseException | None,
- exc_tb: TracebackType | None,
- ) -> None:
- """Closes the client."""
- await self.close()
- class APIClientError(Exception):
- pass
- class MissingKeyError(APIClientError):
- def __init__(self, key_name: str) -> None:
- super().__init__(
- f"API key is not set. Please set the `{key_name}` environment variable or "
- f"pass the key directly to the constructor."
- )
- class RequestError(APIClientError):
- def __init__(self, message: str) -> None:
- self.message = message
- super().__init__(f"Problem with API request: {self.message}")
- class ResponseError(APIClientError):
- def __init__(self, status_code: int, message: str) -> None:
- self.status_code = status_code
- self.message = message
- super().__init__(
- f"API responded with an error: {self.status_code} - {self.message}"
- )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement