(1.0.3) retry handlers and more
added automatic retry handlers for 429 and 5xx error codes, as well as custom exceptions for ratelimiting, client errors, and server errors also added a more advanced user agent string constructor. previously, setting FloweryAPIConfig.user_agent would override the default user_agent. so, if you set user_agent to `foobar`, the user agent string would be `foobar` in requests. now, the user agent string sent by requests would be the following on my development machine: `'User-Agent': 'PyFlowery/1.0.3 PyFloweryTests (Python 3.12.6 (main, Sep 8 2024, 13:18:56) [GCC 14.2.1 20240805])'`
This commit is contained in:
parent
d585072d3e
commit
33b63fecc4
8 changed files with 89 additions and 25 deletions
|
@ -1,8 +1,15 @@
|
|||
"""This module contains the RestAdapter class, which is used to make requests to the Flowery API."""""
|
||||
from asyncio import sleep as asleep
|
||||
from json import JSONDecodeError
|
||||
|
||||
import aiohttp
|
||||
|
||||
from pyflowery.exceptions import (
|
||||
ClientError,
|
||||
InternalServerError,
|
||||
RetryLimitExceeded,
|
||||
TooManyRequests,
|
||||
)
|
||||
from pyflowery.models import FloweryAPIConfig, Result
|
||||
|
||||
|
||||
|
@ -17,8 +24,7 @@ class RestAdapter:
|
|||
"""
|
||||
def __init__(self, config = FloweryAPIConfig):
|
||||
self._url = "https://api.flowery.pw/v1"
|
||||
self._user_agent = config.user_agent
|
||||
self._logger = config.logger
|
||||
self.config = config
|
||||
|
||||
async def _do(self, http_method: str, endpoint: str, params: dict = None, timeout: float = 60):
|
||||
"""Internal method to make a request to the Flowery API. You shouldn't use this directly.
|
||||
|
@ -34,26 +40,45 @@ class RestAdapter:
|
|||
"""
|
||||
full_url = self._url + endpoint
|
||||
headers = {
|
||||
'User-Agent': self._user_agent,
|
||||
'User-Agent': self.config.prepended_user_agent(),
|
||||
}
|
||||
sanitized_params = {k: str(v) if isinstance(v, bool) else v for k, v in params.items()} if params else None
|
||||
self._logger.debug("Making %s request to %s with params %s", http_method, full_url, sanitized_params)
|
||||
retry_counter = 0
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.request(method=http_method, url=full_url, params=sanitized_params, headers=headers, timeout=timeout) as response:
|
||||
try:
|
||||
data = await response.json()
|
||||
except (JSONDecodeError, aiohttp.ContentTypeError):
|
||||
data = await response.read()
|
||||
while retry_counter < self.config.retry_limit:
|
||||
self.config.logger.debug("Making %s request to %s with headers %s and params %s", http_method, full_url, headers, sanitized_params)
|
||||
async with session.request(method=http_method, url=full_url, params=sanitized_params, headers=headers, timeout=timeout) as response:
|
||||
try:
|
||||
data = await response.json()
|
||||
except (JSONDecodeError, aiohttp.ContentTypeError):
|
||||
data = await response.read()
|
||||
|
||||
result = Result(
|
||||
success=response.status in range(200, 299),
|
||||
status_code=response.status,
|
||||
message=response.reason,
|
||||
data=data,
|
||||
)
|
||||
self._logger.debug("Received response: %s %s", response.status, response.reason)
|
||||
return result
|
||||
result = Result(
|
||||
success=response.status,
|
||||
status_code=response.status,
|
||||
message=response.reason,
|
||||
data=data,
|
||||
)
|
||||
self.config.logger.debug("Received response: %s %s", response.status, response.reason)
|
||||
try:
|
||||
if result.status_code == 429:
|
||||
raise TooManyRequests(f"{result.message} - {result.data}")
|
||||
elif 400 <= result.status_code < 500:
|
||||
raise ClientError(f"{result.status_code} - {result.message} - {result.data}")
|
||||
elif 500 <= result.status_code < 600:
|
||||
raise InternalServerError(f"{result.status_code} - {result.message} - {result.data}")
|
||||
else:
|
||||
pass
|
||||
except (TooManyRequests, InternalServerError) as e:
|
||||
if retry_counter < self.config.retry_limit:
|
||||
interval = self.config.interval * retry_counter
|
||||
self.config.logger.error("%s - retrying in %s seconds", e, interval, exc_info=True)
|
||||
retry_counter += 1
|
||||
await asleep(interval)
|
||||
continue
|
||||
raise RetryLimitExceeded(message=f"Request failed more than {self.config.retry_limit} times, not retrying") from e
|
||||
return result
|
||||
|
||||
async def get(self, endpoint: str, params: dict = None, timeout: float = 60) -> Result:
|
||||
"""Make a GET request to the Flowery API. You should almost never have to use this directly.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue