@@ -182,35 +182,94 @@ def test_constructor_options(self):
182
182
assert retry_ ._on_error is _some_function
183
183
184
184
def test_with_deadline (self ):
185
- retry_ = retry .Retry ()
185
+ retry_ = retry .Retry (
186
+ predicate = mock .sentinel .predicate ,
187
+ initial = 1 ,
188
+ maximum = 2 ,
189
+ multiplier = 3 ,
190
+ deadline = 4 ,
191
+ on_error = mock .sentinel .on_error ,
192
+ )
186
193
new_retry = retry_ .with_deadline (42 )
187
194
assert retry_ is not new_retry
188
195
assert new_retry ._deadline == 42
189
196
197
+ # the rest of the attributes should remain the same
198
+ assert new_retry ._predicate is retry_ ._predicate
199
+ assert new_retry ._initial == retry_ ._initial
200
+ assert new_retry ._maximum == retry_ ._maximum
201
+ assert new_retry ._multiplier == retry_ ._multiplier
202
+ assert new_retry ._on_error is retry_ ._on_error
203
+
190
204
def test_with_predicate (self ):
191
- retry_ = retry .Retry ()
205
+ retry_ = retry .Retry (
206
+ predicate = mock .sentinel .predicate ,
207
+ initial = 1 ,
208
+ maximum = 2 ,
209
+ multiplier = 3 ,
210
+ deadline = 4 ,
211
+ on_error = mock .sentinel .on_error ,
212
+ )
192
213
new_retry = retry_ .with_predicate (mock .sentinel .predicate )
193
214
assert retry_ is not new_retry
194
215
assert new_retry ._predicate == mock .sentinel .predicate
195
216
217
+ # the rest of the attributes should remain the same
218
+ assert new_retry ._deadline == retry_ ._deadline
219
+ assert new_retry ._initial == retry_ ._initial
220
+ assert new_retry ._maximum == retry_ ._maximum
221
+ assert new_retry ._multiplier == retry_ ._multiplier
222
+ assert new_retry ._on_error is retry_ ._on_error
223
+
196
224
def test_with_delay_noop (self ):
197
- retry_ = retry .Retry ()
225
+ retry_ = retry .Retry (
226
+ predicate = mock .sentinel .predicate ,
227
+ initial = 1 ,
228
+ maximum = 2 ,
229
+ multiplier = 3 ,
230
+ deadline = 4 ,
231
+ on_error = mock .sentinel .on_error ,
232
+ )
198
233
new_retry = retry_ .with_delay ()
199
234
assert retry_ is not new_retry
200
235
assert new_retry ._initial == retry_ ._initial
201
236
assert new_retry ._maximum == retry_ ._maximum
202
237
assert new_retry ._multiplier == retry_ ._multiplier
203
238
204
239
def test_with_delay (self ):
205
- retry_ = retry .Retry ()
240
+ retry_ = retry .Retry (
241
+ predicate = mock .sentinel .predicate ,
242
+ initial = 1 ,
243
+ maximum = 2 ,
244
+ multiplier = 3 ,
245
+ deadline = 4 ,
246
+ on_error = mock .sentinel .on_error ,
247
+ )
206
248
new_retry = retry_ .with_delay (initial = 1 , maximum = 2 , multiplier = 3 )
207
249
assert retry_ is not new_retry
208
250
assert new_retry ._initial == 1
209
251
assert new_retry ._maximum == 2
210
252
assert new_retry ._multiplier == 3
211
253
254
+ # the rest of the attributes should remain the same
255
+ assert new_retry ._deadline == retry_ ._deadline
256
+ assert new_retry ._predicate is retry_ ._predicate
257
+ assert new_retry ._on_error is retry_ ._on_error
258
+
212
259
def test___str__ (self ):
213
- retry_ = retry .Retry ()
260
+ def if_exception_type (exc ):
261
+ return bool (exc ) # pragma: NO COVER
262
+
263
+ # Explicitly set all attributes as changed Retry defaults should not
264
+ # cause this test to start failing.
265
+ retry_ = retry .Retry (
266
+ predicate = if_exception_type ,
267
+ initial = 1.0 ,
268
+ maximum = 60.0 ,
269
+ multiplier = 2.0 ,
270
+ deadline = 120.0 ,
271
+ on_error = None ,
272
+ )
214
273
assert re .match (
215
274
(
216
275
r"<Retry predicate=<function.*?if_exception_type.*?>, "
@@ -259,6 +318,54 @@ def test___call___and_execute_retry(self, sleep, uniform):
259
318
sleep .assert_called_once_with (retry_ ._initial )
260
319
assert on_error .call_count == 1
261
320
321
+ # Make uniform return half of its maximum, which is the calculated sleep time.
322
+ @mock .patch ("random.uniform" , autospec = True , side_effect = lambda m , n : n / 2.0 )
323
+ @mock .patch ("time.sleep" , autospec = True )
324
+ def test___call___and_execute_retry_hitting_deadline (self , sleep , uniform ):
325
+
326
+ on_error = mock .Mock (spec = ["__call__" ], side_effect = [None ] * 10 )
327
+ retry_ = retry .Retry (
328
+ predicate = retry .if_exception_type (ValueError ),
329
+ initial = 1.0 ,
330
+ maximum = 1024.0 ,
331
+ multiplier = 2.0 ,
332
+ deadline = 9.9 ,
333
+ )
334
+
335
+ utcnow = datetime .datetime .utcnow ()
336
+ utcnow_patcher = mock .patch (
337
+ "google.api_core.datetime_helpers.utcnow" , return_value = utcnow
338
+ )
339
+
340
+ target = mock .Mock (spec = ["__call__" ], side_effect = [ValueError ()] * 10 )
341
+ # __name__ is needed by functools.partial.
342
+ target .__name__ = "target"
343
+
344
+ decorated = retry_ (target , on_error = on_error )
345
+ target .assert_not_called ()
346
+
347
+ with utcnow_patcher as patched_utcnow :
348
+ # Make sure that calls to fake time.sleep() also advance the mocked
349
+ # time clock.
350
+ def increase_time (sleep_delay ):
351
+ patched_utcnow .return_value += datetime .timedelta (seconds = sleep_delay )
352
+ sleep .side_effect = increase_time
353
+
354
+ with pytest .raises (exceptions .RetryError ):
355
+ decorated ("meep" )
356
+
357
+ assert target .call_count == 5
358
+ target .assert_has_calls ([mock .call ("meep" )] * 5 )
359
+ assert on_error .call_count == 5
360
+
361
+ # check the delays
362
+ assert sleep .call_count == 4 # once between each successive target calls
363
+ last_wait = sleep .call_args .args [0 ]
364
+ total_wait = sum (call_args .args [0 ] for call_args in sleep .call_args_list )
365
+
366
+ assert last_wait == 2.9 # and not 8.0, because the last delay was shortened
367
+ assert total_wait == 9.9 # the same as the deadline
368
+
262
369
@mock .patch ("time.sleep" , autospec = True )
263
370
def test___init___without_retry_executed (self , sleep ):
264
371
_some_function = mock .Mock ()
0 commit comments