python-backoff 2.3.1__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
backoff/_decorator.py CHANGED
@@ -1,39 +1,51 @@
1
+ from __future__ import annotations
2
+
1
3
  import inspect
2
4
  import logging
3
5
  import operator
4
- from typing import Any, Callable, Iterable, Optional, Type, Union
6
+ from typing import TYPE_CHECKING, Any, cast
5
7
 
8
+ from backoff import _async, _sync
6
9
  from backoff._common import (
7
- _prepare_logger,
8
10
  _config_handlers,
9
11
  _log_backoff,
12
+ _log_backoff_context,
10
13
  _log_giveup,
14
+ _log_giveup_context,
15
+ _prepare_logger,
11
16
  )
12
17
  from backoff._jitter import full_jitter
13
- from backoff import _async, _sync
14
- from backoff._typing import (
15
- _CallableT,
16
- _Handler,
17
- _Jitterer,
18
- _MaybeCallable,
19
- _MaybeLogger,
20
- _MaybeSequence,
21
- _Predicate,
22
- _WaitGenerator,
23
- )
18
+ from backoff._wait_gen import expo
19
+
20
+ if TYPE_CHECKING:
21
+ from collections.abc import AsyncGenerator, Callable, Generator, Iterable
22
+
23
+ from backoff._common import Attempt
24
+ from backoff._typing import (
25
+ _AnyLoggerOrName,
26
+ _CallableT,
27
+ _ContextHandler,
28
+ _Handler,
29
+ _Jitterer,
30
+ _MaybeCallable,
31
+ _MaybeTuple,
32
+ _Predicate,
33
+ _WaitGenerator,
34
+ )
24
35
 
25
36
 
26
37
  def on_predicate(
27
38
  wait_gen: _WaitGenerator,
28
39
  predicate: _Predicate[Any] = operator.not_,
29
40
  *,
30
- max_tries: Optional[_MaybeCallable[int]] = None,
31
- max_time: Optional[_MaybeCallable[float]] = None,
32
- jitter: Union[_Jitterer, None] = full_jitter,
33
- on_success: Union[_Handler, Iterable[_Handler], None] = None,
34
- on_backoff: Union[_Handler, Iterable[_Handler], None] = None,
35
- on_giveup: Union[_Handler, Iterable[_Handler], None] = None,
36
- logger: _MaybeLogger = "backoff",
41
+ max_tries: _MaybeCallable[int] | None = None,
42
+ max_time: _MaybeCallable[float] | None = None,
43
+ jitter: _Jitterer | None = full_jitter,
44
+ on_try: _Handler | Iterable[_Handler] | None = None,
45
+ on_success: _Handler | Iterable[_Handler] | None = None,
46
+ on_backoff: _Handler | Iterable[_Handler] | None = None,
47
+ on_giveup: _Handler | Iterable[_Handler] | None = None,
48
+ logger: _AnyLoggerOrName | None = "backoff",
37
49
  backoff_log_level: int = logging.INFO,
38
50
  giveup_log_level: int = logging.ERROR,
39
51
  **wait_gen_kwargs: Any,
@@ -62,6 +74,9 @@ def on_predicate(
62
74
  concurrent clients. Wait times are jittered by default
63
75
  using the full_jitter function. Jittering may be disabled
64
76
  altogether by passing jitter=None.
77
+ on_try: Callable (or iterable of callables) with a unary
78
+ signature to be called before each attempt. The parameter
79
+ is a dict containing details about the invocation.
65
80
  on_success: Callable (or iterable of callables) with a unary
66
81
  signature to be called in the event of success. The
67
82
  parameter is a dict containing details about the invocation.
@@ -82,10 +97,11 @@ def on_predicate(
82
97
  This is useful for runtime configuration.
83
98
  """
84
99
 
85
- def decorate(target):
86
- nonlocal logger, on_success, on_backoff, on_giveup
100
+ def decorate(target: _CallableT) -> _CallableT:
101
+ nonlocal logger, on_try, on_success, on_backoff, on_giveup
87
102
 
88
103
  logger = _prepare_logger(logger)
104
+ on_try = _config_handlers(on_try)
89
105
  on_success = _config_handlers(on_success)
90
106
  on_backoff = _config_handlers(
91
107
  on_backoff,
@@ -101,21 +117,38 @@ def on_predicate(
101
117
  )
102
118
 
103
119
  if inspect.iscoroutinefunction(target):
104
- retry = _async.retry_predicate
105
- else:
106
- retry = _sync.retry_predicate
107
-
108
- return retry(
109
- target,
110
- wait_gen,
111
- predicate,
112
- max_tries=max_tries,
113
- max_time=max_time,
114
- jitter=jitter,
115
- on_success=on_success,
116
- on_backoff=on_backoff,
117
- on_giveup=on_giveup,
118
- wait_gen_kwargs=wait_gen_kwargs,
120
+ return cast(
121
+ "_CallableT",
122
+ _async.retry_predicate(
123
+ target,
124
+ wait_gen,
125
+ predicate,
126
+ max_tries=max_tries,
127
+ max_time=max_time,
128
+ jitter=jitter,
129
+ on_try=on_try,
130
+ on_success=on_success,
131
+ on_backoff=on_backoff,
132
+ on_giveup=on_giveup,
133
+ wait_gen_kwargs=wait_gen_kwargs,
134
+ ),
135
+ )
136
+
137
+ return cast(
138
+ "_CallableT",
139
+ _sync.retry_predicate(
140
+ target,
141
+ wait_gen,
142
+ predicate,
143
+ max_tries=max_tries,
144
+ max_time=max_time,
145
+ jitter=jitter,
146
+ on_try=on_try,
147
+ on_success=on_success,
148
+ on_backoff=on_backoff,
149
+ on_giveup=on_giveup,
150
+ wait_gen_kwargs=wait_gen_kwargs,
151
+ ),
119
152
  )
120
153
 
121
154
  # Return a function which decorates a target with a retry loop.
@@ -124,17 +157,18 @@ def on_predicate(
124
157
 
125
158
  def on_exception(
126
159
  wait_gen: _WaitGenerator,
127
- exception: _MaybeSequence[Type[Exception]],
160
+ exception: _MaybeTuple[type[Exception]],
128
161
  *,
129
- max_tries: Optional[_MaybeCallable[int]] = None,
130
- max_time: Optional[_MaybeCallable[float]] = None,
131
- jitter: Union[_Jitterer, None] = full_jitter,
162
+ max_tries: _MaybeCallable[int] | None = None,
163
+ max_time: _MaybeCallable[float] | None = None,
164
+ jitter: _Jitterer | None = full_jitter,
132
165
  giveup: _Predicate[Exception] = lambda e: False,
133
- on_success: Union[_Handler, Iterable[_Handler], None] = None,
134
- on_backoff: Union[_Handler, Iterable[_Handler], None] = None,
135
- on_giveup: Union[_Handler, Iterable[_Handler], None] = None,
166
+ on_try: _Handler | Iterable[_Handler] | None = None,
167
+ on_success: _Handler | Iterable[_Handler] | None = None,
168
+ on_backoff: _Handler | Iterable[_Handler] | None = None,
169
+ on_giveup: _Handler | Iterable[_Handler] | None = None,
136
170
  raise_on_giveup: bool = True,
137
- logger: _MaybeLogger = "backoff",
171
+ logger: _AnyLoggerOrName | None = "backoff",
138
172
  backoff_log_level: int = logging.INFO,
139
173
  giveup_log_level: int = logging.ERROR,
140
174
  **wait_gen_kwargs: Any,
@@ -164,6 +198,9 @@ def on_exception(
164
198
  giveup: Function accepting an exception instance and
165
199
  returning whether or not to give up. Optional. The default
166
200
  is to always continue.
201
+ on_try: Callable (or iterable of callables) with a unary
202
+ signature to be called before each attempt. The parameter
203
+ is a dict containing details about the invocation.
167
204
  on_success: Callable (or iterable of callables) with a unary
168
205
  signature to be called in the event of success. The
169
206
  parameter is a dict containing details about the invocation.
@@ -185,10 +222,11 @@ def on_exception(
185
222
  This is useful for runtime configuration.
186
223
  """
187
224
 
188
- def decorate(target):
189
- nonlocal logger, on_success, on_backoff, on_giveup
225
+ def decorate(target: _CallableT) -> _CallableT:
226
+ nonlocal logger, on_try, on_success, on_backoff, on_giveup
190
227
 
191
228
  logger = _prepare_logger(logger)
229
+ on_try = _config_handlers(on_try)
192
230
  on_success = _config_handlers(on_success)
193
231
  on_backoff = _config_handlers(
194
232
  on_backoff,
@@ -204,24 +242,201 @@ def on_exception(
204
242
  )
205
243
 
206
244
  if inspect.iscoroutinefunction(target):
207
- retry = _async.retry_exception
208
- else:
209
- retry = _sync.retry_exception
210
-
211
- return retry(
212
- target,
213
- wait_gen,
214
- exception,
215
- max_tries=max_tries,
216
- max_time=max_time,
217
- jitter=jitter,
218
- giveup=giveup,
219
- on_success=on_success,
220
- on_backoff=on_backoff,
221
- on_giveup=on_giveup,
222
- raise_on_giveup=raise_on_giveup,
223
- wait_gen_kwargs=wait_gen_kwargs,
245
+ return cast(
246
+ "_CallableT",
247
+ _async.retry_exception(
248
+ target,
249
+ wait_gen,
250
+ exception,
251
+ max_tries=max_tries,
252
+ max_time=max_time,
253
+ jitter=jitter,
254
+ giveup=giveup,
255
+ on_try=on_try,
256
+ on_success=on_success,
257
+ on_backoff=on_backoff,
258
+ on_giveup=on_giveup,
259
+ raise_on_giveup=raise_on_giveup,
260
+ wait_gen_kwargs=wait_gen_kwargs,
261
+ ),
262
+ )
263
+
264
+ return cast(
265
+ "_CallableT",
266
+ _sync.retry_exception(
267
+ target,
268
+ wait_gen,
269
+ exception,
270
+ max_tries=max_tries,
271
+ max_time=max_time,
272
+ jitter=jitter,
273
+ giveup=giveup,
274
+ on_try=on_try,
275
+ on_success=on_success,
276
+ on_backoff=on_backoff,
277
+ on_giveup=on_giveup,
278
+ raise_on_giveup=raise_on_giveup,
279
+ wait_gen_kwargs=wait_gen_kwargs,
280
+ ),
224
281
  )
225
282
 
226
283
  # Return a function which decorates a target with a retry loop.
227
284
  return decorate
285
+
286
+
287
+ def retry_context(
288
+ exception: _MaybeTuple[type[Exception]] = Exception,
289
+ wait_gen: _WaitGenerator = expo,
290
+ *,
291
+ max_tries: _MaybeCallable[int] | None = None,
292
+ max_time: _MaybeCallable[float] | None = None,
293
+ jitter: _Jitterer | None = full_jitter,
294
+ giveup: _Predicate[BaseException] = lambda e: False,
295
+ on_try: _ContextHandler | Iterable[_ContextHandler] | None = None,
296
+ on_success: _ContextHandler | Iterable[_ContextHandler] | None = None,
297
+ on_backoff: _ContextHandler | Iterable[_ContextHandler] | None = None,
298
+ on_giveup: _ContextHandler | Iterable[_ContextHandler] | None = None,
299
+ raise_on_giveup: bool = True,
300
+ logger: _AnyLoggerOrName | None = "backoff",
301
+ backoff_log_level: int = logging.INFO,
302
+ giveup_log_level: int = logging.ERROR,
303
+ **wait_gen_kwargs: Any,
304
+ ) -> Generator[Attempt, None, None]:
305
+ """Returns a generator of retry attempts, for direct use with a `for` loop.
306
+
307
+ Unlike `on_exception`, this doesn't wrap a whole function; it lets a
308
+ caller retry an arbitrary block of code:
309
+
310
+ for attempt in backoff.retry_context(ValueError, backoff.expo):
311
+ with attempt:
312
+ do_something()
313
+
314
+ Each `attempt` is a context manager: exceptions matching `exception`
315
+ are caught, and the loop either sleeps and retries or lets the
316
+ exception (or a different one) propagate once retries are exhausted.
317
+ Succeeding (no exception raised in the `with` block) ends the loop.
318
+
319
+ Args:
320
+ exception: An exception type (or tuple of types) which triggers
321
+ backoff.
322
+ wait_gen: A generator yielding successive wait times in seconds.
323
+ max_tries: The maximum number of attempts to make before giving
324
+ up. Once exhausted, the exception will be allowed to escape.
325
+ The default value of None means there is no limit to the
326
+ number of tries. If a callable is passed, it will be
327
+ evaluated at runtime and its return value used.
328
+ max_time: The maximum total amount of time to try for before
329
+ giving up. Once expired, the exception will be allowed to
330
+ escape. If a callable is passed, it will be evaluated at
331
+ runtime and its return value used.
332
+ jitter: A function of the value yielded by wait_gen returning
333
+ the actual time to wait. Jittered by default using
334
+ full_jitter; disable with jitter=None.
335
+ giveup: Function accepting an exception instance and returning
336
+ whether or not to give up. Optional. The default is to
337
+ always continue.
338
+ on_success: Callable (or iterable of callables) with a unary
339
+ signature called on success. The parameter is a dict with
340
+ `tries` and `elapsed`.
341
+ on_backoff: Callable (or iterable of callables) called on
342
+ backoff. The parameter dict additionally has `wait` and
343
+ `exception`.
344
+ on_giveup: Callable (or iterable of callables) called when
345
+ giving up. The parameter dict additionally has `exception`.
346
+ raise_on_giveup: Boolean indicating whether the registered
347
+ exception should be raised on giveup. Defaults to `True`.
348
+ logger: Name or Logger object to log to. Defaults to 'backoff'.
349
+ backoff_log_level: log level for the backoff event. Defaults to "INFO"
350
+ giveup_log_level: log level for the give up event. Defaults to "ERROR"
351
+ **wait_gen_kwargs: Any additional keyword args specified will be
352
+ passed to wait_gen when it is initialized.
353
+ """
354
+ logger = _prepare_logger(logger)
355
+ on_try = _config_handlers(on_try)
356
+ on_success = _config_handlers(on_success)
357
+ on_backoff = _config_handlers(
358
+ on_backoff,
359
+ default_handler=_log_backoff_context,
360
+ logger=logger,
361
+ log_level=backoff_log_level,
362
+ )
363
+ on_giveup = _config_handlers(
364
+ on_giveup,
365
+ default_handler=_log_giveup_context,
366
+ logger=logger,
367
+ log_level=giveup_log_level,
368
+ )
369
+
370
+ return _sync.retry_context(
371
+ exception,
372
+ wait_gen,
373
+ max_tries=max_tries,
374
+ max_time=max_time,
375
+ jitter=jitter,
376
+ giveup=giveup,
377
+ on_try=on_try,
378
+ on_success=on_success,
379
+ on_backoff=on_backoff,
380
+ on_giveup=on_giveup,
381
+ raise_on_giveup=raise_on_giveup,
382
+ wait_gen_kwargs=wait_gen_kwargs,
383
+ )
384
+
385
+
386
+ def aretry_context(
387
+ exception: _MaybeTuple[type[Exception]] = Exception,
388
+ wait_gen: _WaitGenerator = expo,
389
+ *,
390
+ max_tries: _MaybeCallable[int] | None = None,
391
+ max_time: _MaybeCallable[float] | None = None,
392
+ jitter: _Jitterer | None = full_jitter,
393
+ giveup: _Predicate[BaseException] = lambda e: False,
394
+ on_try: _ContextHandler | Iterable[_ContextHandler] | None = None,
395
+ on_success: _ContextHandler | Iterable[_ContextHandler] | None = None,
396
+ on_backoff: _ContextHandler | Iterable[_ContextHandler] | None = None,
397
+ on_giveup: _ContextHandler | Iterable[_ContextHandler] | None = None,
398
+ raise_on_giveup: bool = True,
399
+ logger: _AnyLoggerOrName | None = "backoff",
400
+ backoff_log_level: int = logging.INFO,
401
+ giveup_log_level: int = logging.ERROR,
402
+ **wait_gen_kwargs: Any,
403
+ ) -> AsyncGenerator[Attempt, None]:
404
+ """Async counterpart to `retry_context`, for use with `async for`.
405
+
406
+ async for attempt in backoff.aretry_context(ValueError, backoff.expo):
407
+ with attempt:
408
+ await do_something()
409
+
410
+ `giveup` and the handlers may be sync or async callables. See
411
+ `retry_context` for the full argument reference.
412
+ """
413
+ logger = _prepare_logger(logger)
414
+ on_try = _config_handlers(on_try)
415
+ on_success = _config_handlers(on_success)
416
+ on_backoff = _config_handlers(
417
+ on_backoff,
418
+ default_handler=_log_backoff_context,
419
+ logger=logger,
420
+ log_level=backoff_log_level,
421
+ )
422
+ on_giveup = _config_handlers(
423
+ on_giveup,
424
+ default_handler=_log_giveup_context,
425
+ logger=logger,
426
+ log_level=giveup_log_level,
427
+ )
428
+
429
+ return _async.aretry_context(
430
+ exception,
431
+ wait_gen,
432
+ max_tries=max_tries,
433
+ max_time=max_time,
434
+ jitter=jitter,
435
+ giveup=giveup,
436
+ on_try=on_try,
437
+ on_success=on_success,
438
+ on_backoff=on_backoff,
439
+ on_giveup=on_giveup,
440
+ raise_on_giveup=raise_on_giveup,
441
+ wait_gen_kwargs=wait_gen_kwargs,
442
+ )