Skip to content

Commit 73de41f

Browse files
committed
🔧 Refactor utils.py to include retry logic and improve error handling
1 parent 3cc3837 commit 73de41f

File tree

1 file changed

+60
-9
lines changed

1 file changed

+60
-9
lines changed

src/codeboxapi/utils.py

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"""Utility functions for API requests"""
22

33
import json
4+
from asyncio import sleep as asleep
45
from io import BytesIO
6+
from time import sleep
57
from typing import Optional
68

79
import requests
8-
from aiohttp import ClientResponse, ClientSession, FormData
10+
from aiohttp import ClientError, ClientResponse, ClientSession, FormData
911
from aiohttp.payload import BytesIOPayload
1012

1113
from codeboxapi.config import settings
@@ -48,9 +50,13 @@ def handle_response(response: requests.Response):
4850
response.headers["Content-Type"].split(";")[0], lambda r: r.content.decode()
4951
)
5052
if response.status_code != 200:
53+
try:
54+
json_body = response.json()
55+
except Exception:
56+
json_body = {"": response.text}
5157
raise CodeBoxError(
5258
http_status=response.status_code,
53-
json_body=response.json(),
59+
json_body=json_body,
5460
headers=dict(response.headers.items()),
5561
)
5662
return handler(response)
@@ -100,13 +106,35 @@ def base_request(
100106
endpoint: str,
101107
body: Optional[dict] = None,
102108
files: Optional[dict] = None,
109+
retries: int = 3,
110+
backoff_factor: float = 0.3,
103111
) -> dict:
104112
"""
105-
Makes a request to the CodeBox API.
113+
Makes a request to the CodeBox API with retry logic.
114+
115+
Args:
116+
- method: HTTP method as a string.
117+
- endpoint: API endpoint as a string.
118+
- body: Optional dictionary containing the JSON body.
119+
- files: Optional dictionary containing file data.
120+
- retries: Maximum number of retries on failure.
121+
- backoff_factor: Multiplier for delay between retries (exponential backoff).
122+
123+
Returns:
124+
- A dictionary response from the API.
106125
"""
107126
request_data = build_request_data(method, endpoint, body, files)
108-
response = requests.request(**request_data, timeout=270)
109-
return handle_response(response)
127+
for attempt in range(retries):
128+
try:
129+
response = requests.request(**request_data, timeout=270)
130+
return handle_response(response)
131+
except requests.RequestException as e:
132+
if attempt < retries - 1:
133+
sleep_time = backoff_factor * (2**attempt)
134+
sleep(sleep_time)
135+
else:
136+
raise e
137+
raise CodeBoxError(http_status=500, json_body={"error": "Max retries exceeded"})
110138

111139

112140
async def abase_request(
@@ -115,9 +143,23 @@ async def abase_request(
115143
endpoint: str,
116144
body: Optional[dict] = None,
117145
files: Optional[dict] = None,
146+
retries: int = 3,
147+
backoff_factor: float = 0.3,
118148
) -> dict:
119149
"""
120-
Makes an asynchronous request to the CodeBox API.
150+
Makes an asynchronous request to the CodeBox API with retry functionality.
151+
152+
Args:
153+
- session: The aiohttp ClientSession.
154+
- method: HTTP method as a string.
155+
- endpoint: API endpoint as a string.
156+
- body: Optional dictionary containing the JSON body.
157+
- files: Optional dictionary containing file data.
158+
- retries: Maximum number of retries on failure.
159+
- backoff_factor: Multiplier for delay between retries (exponential backoff).
160+
161+
Returns:
162+
- A dictionary response from the API.
121163
"""
122164
request_data = build_request_data(method, endpoint, body, files)
123165
if files is not None:
@@ -133,11 +175,20 @@ async def abase_request(
133175
request_data.pop("files")
134176
request_data.pop("json")
135177
request_data["data"] = data
136-
response = await session.request(**request_data)
137178
else:
138179
request_data.pop("files")
139-
response = await session.request(**request_data)
140-
return await handle_response_async(response)
180+
181+
for attempt in range(retries):
182+
try:
183+
response = await session.request(**request_data)
184+
return await handle_response_async(response)
185+
except ClientError as e:
186+
if attempt < retries - 1:
187+
sleep_time = backoff_factor * (2**attempt)
188+
await asleep(sleep_time)
189+
else:
190+
raise e
191+
raise CodeBoxError(http_status=500, json_body={"error": "Max retries exceeded"})
141192

142193

143194
def set_api_key(api_key: str) -> None:

0 commit comments

Comments
 (0)