@@ -32,6 +32,15 @@ class AsyncClient:
32
32
The loop to be used. Defaults to :meth:`asyncio.get_event_loop`.
33
33
ignore_warning: Optional[:class:`bool`]
34
34
Ignores the ``I-Am-Testing`` Warning. Defaults to ``False``.
35
+ handle_ratelimit: Optional[:class:`bool`]
36
+ Handles the ratelimit. If this is ``False``, then it raises
37
+ :exc:`TooManyRequests`. Else, it will sleep for `Retry-After`.
38
+ Defaults to ``True``.
39
+ tries: Optional[:class:`int`]
40
+ The number of tries to execute a request to the API This is to.
41
+ handle 429s. This does not affect anything if ``handle_ratelimit``
42
+ is ``False``. If this is ``None``, it will go infinitely and you
43
+ might get Temp-Banned by Cloudflare. Defaults to ``5``.
35
44
36
45
Attributes
37
46
----------
@@ -43,21 +52,25 @@ class AsyncClient:
43
52
The session used. ``None`` if not specified.
44
53
"""
45
54
46
- def __init__ (self , token : str = 'I-Am-Testing' , * , session : aiohttp .ClientSession = None , loop : asyncio .AbstractEventLoop = None , ignore_warning : bool = False ):
55
+ def __init__ (self , token : str = 'I-Am-Testing' , * , session : aiohttp .ClientSession = None , loop : asyncio .AbstractEventLoop = None , ignore_warning : bool = False , handle_ratelimit : bool = True , tries : int = 5 ):
47
56
if not token :
48
57
raise NoTokenProvided ()
49
58
elif token == 'I-Am-Testing' and not ignore_warning :
50
59
warnings .warn ('Using I-Am-Testing token will only let you 5 requests/day (UTC based, will reset on 00:00 UTC) for all `/api` methods and will raise an `openrobot.api_wrapper.error.Forbidden` after you have reached your limit.' )
51
60
52
- self .token = str (token )
61
+ self .token : str = str (token )
53
62
54
- self .loop = loop or asyncio .get_event_loop ()
63
+ self .loop : asyncio . AbstractEventLoop = loop or asyncio .get_event_loop ()
55
64
56
- self .session = session if isinstance (session , aiohttp .ClientSession ) else None
65
+ self .session : typing . Optional [ aiohttp . ClientSession ] = session if isinstance (session , aiohttp .ClientSession ) else None
57
66
58
67
if self .session :
59
68
self .session ._loop = self .loop
60
69
70
+ self .handle_ratelimit : bool = handle_ratelimit
71
+
72
+ self .tries : int = tries
73
+
61
74
# Important and internal methods, but should be used un-regularly by the User itself.
62
75
63
76
def _get_authorization_headers (self , token : str = None , * , header = True ):
@@ -90,35 +103,12 @@ async def _request(self, method: str, url: str, **kwargs) -> typing.Union[dict,
90
103
url = ('https://api.openrobot.xyz/api' + url )
91
104
else :
92
105
raise TypeError ('URL is not a valid HTTP/HTTPs URL.' )
93
-
94
- if self .session :
95
- async with self .session .request (method , url , ** kwargs ) as resp :
96
- js = await resp .json ()
97
- if resp .status in return_on :
98
- if raw :
99
- return resp
100
- else :
101
- return js
102
- elif resp .status == 403 :
103
- raise Forbidden (resp , js )
104
- elif resp .status == 400 :
105
- raise BadRequest (resp , js )
106
- elif resp .status == 500 :
107
- raise InternalServerError (resp , js )
108
- elif 200 <= resp .status < 300 :
109
- if raw :
110
- return resp
111
- else :
112
- return js
113
- else :
114
- cls = OpenRobotAPIError (js )
115
- cls .raw = js
116
- cls .response = resp
117
106
118
- raise cls
119
- else :
120
- async with aiohttp .ClientSession (loop = self .loop ) as sess :
121
- async with sess .request (method , url , ** kwargs ) as resp :
107
+ tries = int (self .tries ) if self .tries is not None else None
108
+
109
+ while tries is None or tries > 0 :
110
+ if self .session :
111
+ async with self .session .request (method , url , ** kwargs ) as resp :
122
112
js = await resp .json ()
123
113
if resp .status in return_on :
124
114
if raw :
@@ -131,6 +121,17 @@ async def _request(self, method: str, url: str, **kwargs) -> typing.Union[dict,
131
121
raise BadRequest (resp , js )
132
122
elif resp .status == 500 :
133
123
raise InternalServerError (resp , js )
124
+ elif resp .status == 429 :
125
+ if not self .handle_ratelimit :
126
+ raise TooManyRequests (resp , js )
127
+
128
+ try :
129
+ await asyncio .sleep (resp .headers ['Retry-After' ])
130
+ except KeyError as e :
131
+ raise KeyError ('Retry-After header is not present.' ) from e
132
+
133
+ if tries :
134
+ tries -= 1
134
135
elif 200 <= resp .status < 300 :
135
136
if raw :
136
137
return resp
@@ -142,6 +143,45 @@ async def _request(self, method: str, url: str, **kwargs) -> typing.Union[dict,
142
143
cls .response = resp
143
144
144
145
raise cls
146
+ else :
147
+ async with aiohttp .ClientSession (loop = self .loop ) as sess :
148
+ async with sess .request (method , url , ** kwargs ) as resp :
149
+ js = await resp .json ()
150
+ if resp .status in return_on :
151
+ if raw :
152
+ return resp
153
+ else :
154
+ return js
155
+ elif resp .status == 403 :
156
+ raise Forbidden (resp , js )
157
+ elif resp .status == 400 :
158
+ raise BadRequest (resp , js )
159
+ elif resp .status == 500 :
160
+ raise InternalServerError (resp , js )
161
+ elif resp .status == 429 :
162
+ if not self .handle_ratelimit :
163
+ raise TooManyRequests (resp , js )
164
+
165
+ try :
166
+ await asyncio .sleep (resp .headers ['Retry-After' ])
167
+ except KeyError as e :
168
+ raise KeyError ('Retry-After header is not present.' ) from e
169
+
170
+ if tries :
171
+ tries -= 1
172
+ elif 200 <= resp .status < 300 :
173
+ if raw :
174
+ return resp
175
+ else :
176
+ return js
177
+ else :
178
+ cls = OpenRobotAPIError (js )
179
+ cls .raw = js
180
+ cls .response = resp
181
+
182
+ raise cls
183
+
184
+ raise TooManyRequests (resp , js )
145
185
146
186
# Methods to query to API:
147
187
0 commit comments