15
15
16
16
import asyncio
17
17
import inspect
18
+ import logging
18
19
import unittest
19
20
from functools import wraps
20
21
21
- from tenacity import AsyncRetrying , RetryError
22
+ import tenacity
22
23
from tenacity import _asyncio as tasyncio
23
24
from tenacity import retry , stop_after_attempt
24
25
from tenacity .wait import wait_fixed
25
-
26
- from .test_tenacity import NoIOErrorAfterCount , current_time_ms
26
+ from .test_tenacity import CapturingHandler , NoneReturnUntilAfterCount , NoIOErrorAfterCount , current_time_ms
27
27
28
28
29
29
def asynctest (callable_ ):
@@ -67,7 +67,7 @@ async def test_iscoroutinefunction(self):
67
67
@asynctest
68
68
async def test_retry_using_async_retying (self ):
69
69
thing = NoIOErrorAfterCount (5 )
70
- retrying = AsyncRetrying ()
70
+ retrying = tenacity . AsyncRetrying ()
71
71
await retrying (_async_function , thing )
72
72
assert thing .counter == thing .count
73
73
@@ -76,7 +76,7 @@ async def test_stop_after_attempt(self):
76
76
thing = NoIOErrorAfterCount (2 )
77
77
try :
78
78
await _retryable_coroutine_with_2_attempts (thing )
79
- except RetryError :
79
+ except tenacity . RetryError :
80
80
assert thing .counter == 2
81
81
82
82
def test_repr (self ):
@@ -86,6 +86,31 @@ def test_retry_attributes(self):
86
86
assert hasattr (_retryable_coroutine , "retry" )
87
87
assert hasattr (_retryable_coroutine , "retry_with" )
88
88
89
+ @asynctest
90
+ async def test_async_retry_error_callback_handler (self ):
91
+ num_attempts = 3
92
+ self .attempt_counter = 0
93
+
94
+ async def _retry_error_callback_handler (retry_state : tenacity .RetryCallState ):
95
+ _retry_error_callback_handler .called_times += 1
96
+ return retry_state .outcome
97
+
98
+ _retry_error_callback_handler .called_times = 0
99
+
100
+ @retry (
101
+ stop = stop_after_attempt (num_attempts ),
102
+ retry_error_callback = _retry_error_callback_handler ,
103
+ )
104
+ async def _foobar ():
105
+ self .attempt_counter += 1
106
+ raise Exception ("This exception should not be raised" )
107
+
108
+ result = await _foobar ()
109
+
110
+ self .assertEqual (_retry_error_callback_handler .called_times , 1 )
111
+ self .assertEqual (num_attempts , self .attempt_counter )
112
+ self .assertIsInstance (result , tenacity .Future )
113
+
89
114
@asynctest
90
115
async def test_attempt_number_is_correct_for_interleaved_coroutines (self ):
91
116
@@ -125,7 +150,7 @@ async def test_do_max_attempts(self):
125
150
with attempt :
126
151
attempts += 1
127
152
raise Exception
128
- except RetryError :
153
+ except tenacity . RetryError :
129
154
pass
130
155
131
156
assert attempts == 3
@@ -151,11 +176,110 @@ async def test_sleeps(self):
151
176
async for attempt in tasyncio .AsyncRetrying (stop = stop_after_attempt (1 ), wait = wait_fixed (1 )):
152
177
with attempt :
153
178
raise Exception ()
154
- except RetryError :
179
+ except tenacity . RetryError :
155
180
pass
156
181
t = current_time_ms () - start
157
182
self .assertLess (t , 1.1 )
158
183
159
184
185
+ class TestAsyncBeforeAfterAttempts (unittest .TestCase ):
186
+ _attempt_number = 0
187
+
188
+ @asynctest
189
+ async def test_before_attempts (self ):
190
+ TestAsyncBeforeAfterAttempts ._attempt_number = 0
191
+
192
+ async def _before (retry_state ):
193
+ TestAsyncBeforeAfterAttempts ._attempt_number = retry_state .attempt_number
194
+
195
+ @retry (
196
+ wait = tenacity .wait_fixed (1 ),
197
+ stop = tenacity .stop_after_attempt (1 ),
198
+ before = _before ,
199
+ )
200
+ async def _test_before ():
201
+ pass
202
+
203
+ await _test_before ()
204
+
205
+ self .assertTrue (TestAsyncBeforeAfterAttempts ._attempt_number == 1 )
206
+
207
+ @asynctest
208
+ async def test_after_attempts (self ):
209
+ TestAsyncBeforeAfterAttempts ._attempt_number = 0
210
+
211
+ async def _after (retry_state ):
212
+ TestAsyncBeforeAfterAttempts ._attempt_number = retry_state .attempt_number
213
+
214
+ @retry (
215
+ wait = tenacity .wait_fixed (0.1 ),
216
+ stop = tenacity .stop_after_attempt (3 ),
217
+ after = _after ,
218
+ )
219
+ async def _test_after ():
220
+ if TestAsyncBeforeAfterAttempts ._attempt_number < 2 :
221
+ raise Exception ("testing after_attempts handler" )
222
+ else :
223
+ pass
224
+
225
+ await _test_after ()
226
+
227
+ self .assertTrue (TestAsyncBeforeAfterAttempts ._attempt_number == 2 )
228
+
229
+ @asynctest
230
+ async def test_before_sleep (self ):
231
+ async def _before_sleep (retry_state ):
232
+ self .assertGreater (retry_state .next_action .sleep , 0 )
233
+ _before_sleep .attempt_number = retry_state .attempt_number
234
+
235
+ _before_sleep .attempt_number = 0
236
+
237
+ @retry (
238
+ wait = tenacity .wait_fixed (0.01 ),
239
+ stop = tenacity .stop_after_attempt (3 ),
240
+ before_sleep = _before_sleep ,
241
+ )
242
+ async def _test_before_sleep ():
243
+ if _before_sleep .attempt_number < 2 :
244
+ raise Exception ("testing before_sleep_attempts handler" )
245
+
246
+ await _test_before_sleep ()
247
+ self .assertEqual (_before_sleep .attempt_number , 2 )
248
+
249
+ async def _test_before_sleep_log_returns (self , exc_info ):
250
+ thing = NoneReturnUntilAfterCount (2 )
251
+ logger = logging .getLogger (self .id ())
252
+ logger .propagate = False
253
+ logger .setLevel (logging .INFO )
254
+ handler = CapturingHandler ()
255
+ logger .addHandler (handler )
256
+ try :
257
+ _before_sleep = tenacity .before_sleep_log (logger , logging .INFO , exc_info = exc_info )
258
+ _retry = tenacity .retry_if_result (lambda result : result is None )
259
+ retrying = tenacity .AsyncRetrying (
260
+ wait = tenacity .wait_fixed (0.01 ),
261
+ stop = tenacity .stop_after_attempt (3 ),
262
+ retry = _retry ,
263
+ before_sleep = _before_sleep ,
264
+ )
265
+ await retrying (_async_function , thing )
266
+ finally :
267
+ logger .removeHandler (handler )
268
+
269
+ etalon_re = r"^Retrying .* in 0\.01 seconds as it returned None\.$"
270
+ self .assertEqual (len (handler .records ), 2 )
271
+ fmt = logging .Formatter ().format
272
+ self .assertRegex (fmt (handler .records [0 ]), etalon_re )
273
+ self .assertRegex (fmt (handler .records [1 ]), etalon_re )
274
+
275
+ @asynctest
276
+ async def test_before_sleep_log_returns_without_exc_info (self ):
277
+ await self ._test_before_sleep_log_returns (exc_info = False )
278
+
279
+ @asynctest
280
+ async def test_before_sleep_log_returns_with_exc_info (self ):
281
+ await self ._test_before_sleep_log_returns (exc_info = True )
282
+
283
+
160
284
if __name__ == "__main__" :
161
285
unittest .main ()
0 commit comments