python-backoff 2.4.0__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/__init__.py +3 -1
- backoff/_async.py +29 -20
- backoff/_common.py +19 -42
- backoff/_decorator.py +101 -57
- backoff/_sync.py +16 -14
- backoff/_typing.py +13 -23
- backoff/_wait_gen.py +23 -2
- backoff/types.py +2 -1
- {python_backoff-2.4.0.dist-info → python_backoff-3.0.0.dist-info}/METADATA +3 -4
- python_backoff-3.0.0.dist-info/RECORD +14 -0
- {python_backoff-2.4.0.dist-info → python_backoff-3.0.0.dist-info}/WHEEL +1 -1
- python_backoff-2.4.0.dist-info/RECORD +0 -14
- {python_backoff-2.4.0.dist-info → python_backoff-3.0.0.dist-info}/licenses/LICENSE +0 -0
backoff/__init__.py
CHANGED
|
@@ -12,11 +12,13 @@ For examples and full documentation see the README at
|
|
|
12
12
|
https://github.com/python-backoff/backoff
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
from backoff._common import Attempt
|
|
15
16
|
from backoff._decorator import aretry_context, on_exception, on_predicate, retry_context
|
|
16
17
|
from backoff._jitter import full_jitter, random_jitter
|
|
17
18
|
from backoff._wait_gen import constant, decay, expo, fibo, runtime
|
|
18
19
|
|
|
19
20
|
__all__ = [
|
|
21
|
+
"Attempt",
|
|
20
22
|
"aretry_context",
|
|
21
23
|
"constant",
|
|
22
24
|
"decay",
|
|
@@ -30,4 +32,4 @@ __all__ = [
|
|
|
30
32
|
"runtime",
|
|
31
33
|
]
|
|
32
34
|
|
|
33
|
-
__version__ = "
|
|
35
|
+
__version__ = "3.0.0"
|
backoff/_async.py
CHANGED
|
@@ -3,13 +3,15 @@ from __future__ import annotations
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import functools
|
|
5
5
|
import inspect
|
|
6
|
-
from
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
7
8
|
|
|
8
|
-
from backoff._common import
|
|
9
|
+
from backoff._common import Attempt, _RetryState
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
12
|
import sys
|
|
12
13
|
from collections.abc import AsyncGenerator, Coroutine, Iterable
|
|
14
|
+
from typing import ParamSpec
|
|
13
15
|
|
|
14
16
|
from backoff._typing import (
|
|
15
17
|
ContextDetails,
|
|
@@ -25,11 +27,6 @@ if TYPE_CHECKING:
|
|
|
25
27
|
_WaitGenerator,
|
|
26
28
|
)
|
|
27
29
|
|
|
28
|
-
if sys.version_info >= (3, 10):
|
|
29
|
-
from typing import ParamSpec
|
|
30
|
-
else:
|
|
31
|
-
from typing_extensions import ParamSpec
|
|
32
|
-
|
|
33
30
|
if sys.version_info >= (3, 11):
|
|
34
31
|
from typing import Unpack
|
|
35
32
|
else:
|
|
@@ -76,26 +73,27 @@ async def _call_handlers(
|
|
|
76
73
|
"kwargs": kwargs,
|
|
77
74
|
"tries": tries,
|
|
78
75
|
"elapsed": elapsed,
|
|
76
|
+
**extra,
|
|
79
77
|
}
|
|
80
|
-
# pyrefly: ignore [no-matching-overload]
|
|
81
|
-
details.update(extra)
|
|
82
78
|
for handler in handlers:
|
|
83
79
|
await handler(details)
|
|
84
80
|
|
|
85
81
|
|
|
86
82
|
def retry_predicate(
|
|
87
|
-
target: Callable[P, T],
|
|
83
|
+
target: Callable[P, Coroutine[object, object, T]],
|
|
88
84
|
wait_gen: _WaitGenerator,
|
|
89
85
|
predicate: _Predicate[T],
|
|
90
86
|
*,
|
|
91
87
|
max_tries: _MaybeCallable[int] | None,
|
|
92
88
|
max_time: _MaybeCallable[float] | None,
|
|
93
89
|
jitter: _Jitterer | None,
|
|
90
|
+
on_try: Iterable[_Handler],
|
|
94
91
|
on_success: Iterable[_Handler],
|
|
95
92
|
on_backoff: Iterable[_Handler],
|
|
96
93
|
on_giveup: Iterable[_Handler],
|
|
97
94
|
wait_gen_kwargs: dict[str, Any],
|
|
98
|
-
) -> Callable[P, T]:
|
|
95
|
+
) -> Callable[P, Coroutine[object, object, T]]:
|
|
96
|
+
on_try = _ensure_coroutines(on_try)
|
|
99
97
|
on_success = _ensure_coroutines(on_success)
|
|
100
98
|
on_backoff = _ensure_coroutines(on_backoff)
|
|
101
99
|
on_giveup = _ensure_coroutines(on_giveup)
|
|
@@ -116,14 +114,16 @@ def retry_predicate(
|
|
|
116
114
|
)
|
|
117
115
|
while True:
|
|
118
116
|
state.start_attempt()
|
|
119
|
-
ret = await target(*args, **kwargs)
|
|
120
117
|
details: _BaseDetails = {
|
|
121
118
|
"target": target,
|
|
122
119
|
"args": args,
|
|
123
120
|
"kwargs": kwargs,
|
|
124
121
|
"tries": state.tries,
|
|
125
|
-
"elapsed": state.
|
|
122
|
+
"elapsed": state.elapsed,
|
|
126
123
|
}
|
|
124
|
+
await _call_handlers(on_try, **details)
|
|
125
|
+
ret = await target(*args, **kwargs)
|
|
126
|
+
details["elapsed"] = state.record_elapsed()
|
|
127
127
|
|
|
128
128
|
if predicate(ret):
|
|
129
129
|
if state.exhausted():
|
|
@@ -154,7 +154,7 @@ def retry_predicate(
|
|
|
154
154
|
|
|
155
155
|
return ret
|
|
156
156
|
|
|
157
|
-
return retry
|
|
157
|
+
return retry
|
|
158
158
|
|
|
159
159
|
|
|
160
160
|
def _adapt_context_handlers(
|
|
@@ -177,7 +177,7 @@ def _adapt_context_handlers(
|
|
|
177
177
|
|
|
178
178
|
|
|
179
179
|
def retry_exception(
|
|
180
|
-
target: Callable[P, T],
|
|
180
|
+
target: Callable[P, Coroutine[object, object, T]],
|
|
181
181
|
wait_gen: _WaitGenerator,
|
|
182
182
|
exception: _MaybeTuple[type[Exception]],
|
|
183
183
|
*,
|
|
@@ -185,12 +185,14 @@ def retry_exception(
|
|
|
185
185
|
max_time: _MaybeCallable[float] | None,
|
|
186
186
|
jitter: _Jitterer | None,
|
|
187
187
|
giveup: _Predicate[Exception],
|
|
188
|
+
on_try: Iterable[_Handler],
|
|
188
189
|
on_success: Iterable[_Handler],
|
|
189
190
|
on_backoff: Iterable[_Handler],
|
|
190
191
|
on_giveup: Iterable[_Handler],
|
|
191
192
|
raise_on_giveup: bool,
|
|
192
193
|
wait_gen_kwargs: dict[str, Any],
|
|
193
|
-
) -> Callable[P, T]:
|
|
194
|
+
) -> Callable[P, Coroutine[object, object, T]]:
|
|
195
|
+
on_try = _ensure_coroutines(on_try)
|
|
194
196
|
on_success = _ensure_coroutines(on_success)
|
|
195
197
|
on_backoff = _ensure_coroutines(on_backoff)
|
|
196
198
|
on_giveup = _ensure_coroutines(on_giveup)
|
|
@@ -214,6 +216,7 @@ def retry_exception(
|
|
|
214
216
|
max_time=max_time,
|
|
215
217
|
jitter=jitter,
|
|
216
218
|
giveup=giveup,
|
|
219
|
+
on_try=_adapt_context_handlers(on_try, target, args, kwargs),
|
|
217
220
|
on_success=_adapt_context_handlers(on_success, target, args, kwargs),
|
|
218
221
|
on_backoff=_adapt_context_handlers(on_backoff, target, args, kwargs),
|
|
219
222
|
on_giveup=_adapt_context_handlers(on_giveup, target, args, kwargs),
|
|
@@ -221,11 +224,11 @@ def retry_exception(
|
|
|
221
224
|
wait_gen_kwargs=wait_gen_kwargs,
|
|
222
225
|
):
|
|
223
226
|
with attempt:
|
|
224
|
-
ret = await target(*args, **kwargs)
|
|
227
|
+
ret = await target(*args, **kwargs)
|
|
225
228
|
|
|
226
229
|
return ret
|
|
227
230
|
|
|
228
|
-
return retry
|
|
231
|
+
return retry
|
|
229
232
|
|
|
230
233
|
|
|
231
234
|
async def _dispatch_handlers(
|
|
@@ -243,12 +246,13 @@ async def aretry_context(
|
|
|
243
246
|
max_time: _MaybeCallable[float] | None,
|
|
244
247
|
jitter: _Jitterer | None,
|
|
245
248
|
giveup: _Predicate[BaseException],
|
|
249
|
+
on_try: Iterable[_ContextHandler],
|
|
246
250
|
on_success: Iterable[_ContextHandler],
|
|
247
251
|
on_backoff: Iterable[_ContextHandler],
|
|
248
252
|
on_giveup: Iterable[_ContextHandler],
|
|
249
253
|
raise_on_giveup: bool,
|
|
250
254
|
wait_gen_kwargs: dict[str, Any],
|
|
251
|
-
) -> AsyncGenerator[
|
|
255
|
+
) -> AsyncGenerator[Attempt, None]:
|
|
252
256
|
giveup = _ensure_coroutine(giveup)
|
|
253
257
|
|
|
254
258
|
state = _RetryState(
|
|
@@ -259,7 +263,12 @@ async def aretry_context(
|
|
|
259
263
|
)
|
|
260
264
|
while True:
|
|
261
265
|
state.start_attempt()
|
|
262
|
-
attempt =
|
|
266
|
+
attempt = Attempt(exception)
|
|
267
|
+
await _dispatch_handlers(
|
|
268
|
+
handlers=on_try,
|
|
269
|
+
tries=state.tries,
|
|
270
|
+
elapsed=state.elapsed,
|
|
271
|
+
)
|
|
263
272
|
yield attempt
|
|
264
273
|
elapsed = state.record_elapsed()
|
|
265
274
|
|
backoff/_common.py
CHANGED
|
@@ -4,16 +4,18 @@ import functools
|
|
|
4
4
|
import logging
|
|
5
5
|
import time
|
|
6
6
|
import traceback
|
|
7
|
-
import
|
|
8
|
-
from typing import TYPE_CHECKING, Any,
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
import sys
|
|
12
|
-
from collections.abc import Generator, Iterable
|
|
12
|
+
from collections.abc import Callable, Generator, Iterable
|
|
13
13
|
|
|
14
14
|
from backoff._typing import (
|
|
15
15
|
ContextDetails,
|
|
16
16
|
Details,
|
|
17
|
+
_AnyLogger,
|
|
18
|
+
_AnyLoggerOrName,
|
|
17
19
|
_ContextHandler,
|
|
18
20
|
_Jitterer,
|
|
19
21
|
_MaybeCallable,
|
|
@@ -25,6 +27,8 @@ if TYPE_CHECKING:
|
|
|
25
27
|
else:
|
|
26
28
|
from typing_extensions import Self
|
|
27
29
|
|
|
30
|
+
ExceptionTypes: TypeAlias = type[Exception] | tuple[type[Exception], ...]
|
|
31
|
+
|
|
28
32
|
|
|
29
33
|
# Use module-specific logger with a default null handler.
|
|
30
34
|
_logger = logging.getLogger("backoff")
|
|
@@ -64,18 +68,7 @@ def _next_wait(
|
|
|
64
68
|
max_time: float | None,
|
|
65
69
|
) -> float:
|
|
66
70
|
value = wait.send(send_value)
|
|
67
|
-
|
|
68
|
-
seconds = jitter(value) if jitter is not None else value
|
|
69
|
-
except TypeError:
|
|
70
|
-
warnings.warn(
|
|
71
|
-
"Nullary jitter function signature is deprecated. Use "
|
|
72
|
-
"unary signature accepting a wait value in seconds and "
|
|
73
|
-
"returning a jittered version of it.",
|
|
74
|
-
DeprecationWarning,
|
|
75
|
-
stacklevel=2,
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
seconds = value + jitter() # type: ignore[call-arg, misc] # ty:ignore[missing-argument, call-non-callable]
|
|
71
|
+
seconds = jitter(value) if jitter is not None else value
|
|
79
72
|
|
|
80
73
|
# don't sleep longer than remaining allotted max_time
|
|
81
74
|
if max_time is not None:
|
|
@@ -132,7 +125,8 @@ class _RetryState:
|
|
|
132
125
|
return _next_wait(self.wait, send_value, jitter, self.elapsed, self.max_time)
|
|
133
126
|
|
|
134
127
|
|
|
135
|
-
|
|
128
|
+
@dataclass(slots=True)
|
|
129
|
+
class Attempt:
|
|
136
130
|
"""A single attempt yielded by `retry_context`/`aretry_context`.
|
|
137
131
|
|
|
138
132
|
Used as `with attempt: ...`. Exceptions matching `exception_types` are
|
|
@@ -141,16 +135,10 @@ class _Attempt:
|
|
|
141
135
|
exception at all, is left for the `with` block's normal exit behavior.
|
|
142
136
|
"""
|
|
143
137
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"exception_types",
|
|
147
|
-
)
|
|
138
|
+
exception_types: ExceptionTypes
|
|
139
|
+
exception: BaseException | None = field(init=False)
|
|
148
140
|
|
|
149
|
-
def
|
|
150
|
-
self,
|
|
151
|
-
exception_types: type[Exception] | tuple[type[Exception], ...],
|
|
152
|
-
) -> None:
|
|
153
|
-
self.exception_types = exception_types
|
|
141
|
+
def __post_init__(self) -> None:
|
|
154
142
|
self.exception: BaseException | None = None
|
|
155
143
|
|
|
156
144
|
def __enter__(self) -> Self:
|
|
@@ -163,7 +151,6 @@ class _Attempt:
|
|
|
163
151
|
tb: object,
|
|
164
152
|
) -> bool:
|
|
165
153
|
if exc_type is None:
|
|
166
|
-
# self.outcome = _Success()
|
|
167
154
|
return False
|
|
168
155
|
|
|
169
156
|
if not issubclass(exc_type, self.exception_types):
|
|
@@ -178,9 +165,7 @@ def _dispatch_handlers(handlers: Iterable[_ContextHandler], **details: Any) -> N
|
|
|
178
165
|
hdlr(details) # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
|
179
166
|
|
|
180
167
|
|
|
181
|
-
def _prepare_logger(
|
|
182
|
-
logger: str | logging.Logger | logging.LoggerAdapter | None,
|
|
183
|
-
) -> logging.Logger | logging.LoggerAdapter | None:
|
|
168
|
+
def _prepare_logger(logger: _AnyLoggerOrName | None) -> _AnyLogger | None:
|
|
184
169
|
if isinstance(logger, str):
|
|
185
170
|
logger = logging.getLogger(logger)
|
|
186
171
|
return logger
|
|
@@ -192,7 +177,7 @@ def _config_handlers(
|
|
|
192
177
|
user_handlers: _HandlerT | Iterable[_HandlerT] | None,
|
|
193
178
|
*,
|
|
194
179
|
default_handler: Callable[..., None] | None = None,
|
|
195
|
-
logger:
|
|
180
|
+
logger: _AnyLogger | None = None,
|
|
196
181
|
log_level: int | None = None,
|
|
197
182
|
) -> list[_HandlerT]:
|
|
198
183
|
handlers: list[_HandlerT] = []
|
|
@@ -225,11 +210,7 @@ def _config_handlers(
|
|
|
225
210
|
|
|
226
211
|
|
|
227
212
|
# Default backoff handler
|
|
228
|
-
def _log_backoff(
|
|
229
|
-
details: Details,
|
|
230
|
-
logger: logging.Logger | logging.LoggerAdapter,
|
|
231
|
-
log_level: int,
|
|
232
|
-
) -> None:
|
|
213
|
+
def _log_backoff(details: Details, logger: _AnyLogger, log_level: int) -> None:
|
|
233
214
|
msg = "Backing off %s(...) for %.1fs (%s)"
|
|
234
215
|
log_args = [details["target"].__name__, details["wait"]] # ty:ignore[unresolved-attribute]
|
|
235
216
|
|
|
@@ -243,11 +224,7 @@ def _log_backoff(
|
|
|
243
224
|
|
|
244
225
|
|
|
245
226
|
# Default giveup handler
|
|
246
|
-
def _log_giveup(
|
|
247
|
-
details: Details,
|
|
248
|
-
logger: logging.Logger | logging.LoggerAdapter,
|
|
249
|
-
log_level: int,
|
|
250
|
-
) -> None:
|
|
227
|
+
def _log_giveup(details: Details, logger: _AnyLogger, log_level: int) -> None:
|
|
251
228
|
msg = "Giving up %s(...) after %d tries (%s)"
|
|
252
229
|
log_args = [details["target"].__name__, details["tries"]] # ty:ignore[unresolved-attribute]
|
|
253
230
|
|
|
@@ -266,7 +243,7 @@ def _log_giveup(
|
|
|
266
243
|
# directly since it's no longer the active exception by this point).
|
|
267
244
|
def _log_backoff_context(
|
|
268
245
|
details: ContextDetails,
|
|
269
|
-
logger:
|
|
246
|
+
logger: _AnyLogger,
|
|
270
247
|
log_level: int,
|
|
271
248
|
) -> None:
|
|
272
249
|
logger.log(
|
|
@@ -280,7 +257,7 @@ def _log_backoff_context(
|
|
|
280
257
|
# Default giveup handler for retry_context/aretry_context.
|
|
281
258
|
def _log_giveup_context(
|
|
282
259
|
details: ContextDetails,
|
|
283
|
-
logger: logging.Logger | logging.LoggerAdapter,
|
|
260
|
+
logger: logging.Logger | logging.LoggerAdapter[Any],
|
|
284
261
|
log_level: int,
|
|
285
262
|
) -> None:
|
|
286
263
|
logger.log(
|
backoff/_decorator.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import inspect
|
|
4
4
|
import logging
|
|
5
5
|
import operator
|
|
6
|
-
from typing import TYPE_CHECKING, Any,
|
|
6
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
7
7
|
|
|
8
8
|
from backoff import _async, _sync
|
|
9
9
|
from backoff._common import (
|
|
@@ -18,29 +18,21 @@ from backoff._jitter import full_jitter
|
|
|
18
18
|
from backoff._wait_gen import expo
|
|
19
19
|
|
|
20
20
|
if TYPE_CHECKING:
|
|
21
|
-
import
|
|
22
|
-
from collections.abc import AsyncGenerator, Generator, Iterable
|
|
21
|
+
from collections.abc import AsyncGenerator, Callable, Generator, Iterable
|
|
23
22
|
|
|
24
|
-
from backoff._common import
|
|
23
|
+
from backoff._common import Attempt
|
|
25
24
|
from backoff._typing import (
|
|
25
|
+
_AnyLoggerOrName,
|
|
26
|
+
_CallableT,
|
|
26
27
|
_ContextHandler,
|
|
27
28
|
_Handler,
|
|
28
29
|
_Jitterer,
|
|
29
30
|
_MaybeCallable,
|
|
30
|
-
_MaybeLogger,
|
|
31
31
|
_MaybeTuple,
|
|
32
32
|
_Predicate,
|
|
33
33
|
_WaitGenerator,
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
if sys.version_info >= (3, 10):
|
|
37
|
-
from typing import ParamSpec
|
|
38
|
-
else:
|
|
39
|
-
from typing_extensions import ParamSpec
|
|
40
|
-
|
|
41
|
-
T = TypeVar("T")
|
|
42
|
-
P = ParamSpec("P")
|
|
43
|
-
|
|
44
36
|
|
|
45
37
|
def on_predicate(
|
|
46
38
|
wait_gen: _WaitGenerator,
|
|
@@ -49,14 +41,15 @@ def on_predicate(
|
|
|
49
41
|
max_tries: _MaybeCallable[int] | None = None,
|
|
50
42
|
max_time: _MaybeCallable[float] | None = None,
|
|
51
43
|
jitter: _Jitterer | None = full_jitter,
|
|
44
|
+
on_try: _Handler | Iterable[_Handler] | None = None,
|
|
52
45
|
on_success: _Handler | Iterable[_Handler] | None = None,
|
|
53
46
|
on_backoff: _Handler | Iterable[_Handler] | None = None,
|
|
54
47
|
on_giveup: _Handler | Iterable[_Handler] | None = None,
|
|
55
|
-
logger:
|
|
48
|
+
logger: _AnyLoggerOrName | None = "backoff",
|
|
56
49
|
backoff_log_level: int = logging.INFO,
|
|
57
50
|
giveup_log_level: int = logging.ERROR,
|
|
58
51
|
**wait_gen_kwargs: Any,
|
|
59
|
-
) -> Callable[[
|
|
52
|
+
) -> Callable[[_CallableT], _CallableT]:
|
|
60
53
|
"""Returns decorator for backoff and retry triggered by predicate.
|
|
61
54
|
|
|
62
55
|
Args:
|
|
@@ -81,6 +74,9 @@ def on_predicate(
|
|
|
81
74
|
concurrent clients. Wait times are jittered by default
|
|
82
75
|
using the full_jitter function. Jittering may be disabled
|
|
83
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.
|
|
84
80
|
on_success: Callable (or iterable of callables) with a unary
|
|
85
81
|
signature to be called in the event of success. The
|
|
86
82
|
parameter is a dict containing details about the invocation.
|
|
@@ -101,10 +97,11 @@ def on_predicate(
|
|
|
101
97
|
This is useful for runtime configuration.
|
|
102
98
|
"""
|
|
103
99
|
|
|
104
|
-
def decorate(target:
|
|
105
|
-
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
|
|
106
102
|
|
|
107
103
|
logger = _prepare_logger(logger)
|
|
104
|
+
on_try = _config_handlers(on_try)
|
|
108
105
|
on_success = _config_handlers(on_success)
|
|
109
106
|
on_backoff = _config_handlers(
|
|
110
107
|
on_backoff,
|
|
@@ -120,21 +117,38 @@ def on_predicate(
|
|
|
120
117
|
)
|
|
121
118
|
|
|
122
119
|
if inspect.iscoroutinefunction(target):
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
+
),
|
|
138
152
|
)
|
|
139
153
|
|
|
140
154
|
# Return a function which decorates a target with a retry loop.
|
|
@@ -149,15 +163,16 @@ def on_exception(
|
|
|
149
163
|
max_time: _MaybeCallable[float] | None = None,
|
|
150
164
|
jitter: _Jitterer | None = full_jitter,
|
|
151
165
|
giveup: _Predicate[Exception] = lambda e: False,
|
|
166
|
+
on_try: _Handler | Iterable[_Handler] | None = None,
|
|
152
167
|
on_success: _Handler | Iterable[_Handler] | None = None,
|
|
153
168
|
on_backoff: _Handler | Iterable[_Handler] | None = None,
|
|
154
169
|
on_giveup: _Handler | Iterable[_Handler] | None = None,
|
|
155
170
|
raise_on_giveup: bool = True,
|
|
156
|
-
logger:
|
|
171
|
+
logger: _AnyLoggerOrName | None = "backoff",
|
|
157
172
|
backoff_log_level: int = logging.INFO,
|
|
158
173
|
giveup_log_level: int = logging.ERROR,
|
|
159
174
|
**wait_gen_kwargs: Any,
|
|
160
|
-
) -> Callable[[
|
|
175
|
+
) -> Callable[[_CallableT], _CallableT]:
|
|
161
176
|
"""Returns decorator for backoff and retry triggered by exception.
|
|
162
177
|
|
|
163
178
|
Args:
|
|
@@ -183,6 +198,9 @@ def on_exception(
|
|
|
183
198
|
giveup: Function accepting an exception instance and
|
|
184
199
|
returning whether or not to give up. Optional. The default
|
|
185
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.
|
|
186
204
|
on_success: Callable (or iterable of callables) with a unary
|
|
187
205
|
signature to be called in the event of success. The
|
|
188
206
|
parameter is a dict containing details about the invocation.
|
|
@@ -204,10 +222,11 @@ def on_exception(
|
|
|
204
222
|
This is useful for runtime configuration.
|
|
205
223
|
"""
|
|
206
224
|
|
|
207
|
-
def decorate(target:
|
|
208
|
-
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
|
|
209
227
|
|
|
210
228
|
logger = _prepare_logger(logger)
|
|
229
|
+
on_try = _config_handlers(on_try)
|
|
211
230
|
on_success = _config_handlers(on_success)
|
|
212
231
|
on_backoff = _config_handlers(
|
|
213
232
|
on_backoff,
|
|
@@ -223,23 +242,42 @@ def on_exception(
|
|
|
223
242
|
)
|
|
224
243
|
|
|
225
244
|
if inspect.iscoroutinefunction(target):
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
+
),
|
|
243
281
|
)
|
|
244
282
|
|
|
245
283
|
# Return a function which decorates a target with a retry loop.
|
|
@@ -254,15 +292,16 @@ def retry_context(
|
|
|
254
292
|
max_time: _MaybeCallable[float] | None = None,
|
|
255
293
|
jitter: _Jitterer | None = full_jitter,
|
|
256
294
|
giveup: _Predicate[BaseException] = lambda e: False,
|
|
295
|
+
on_try: _ContextHandler | Iterable[_ContextHandler] | None = None,
|
|
257
296
|
on_success: _ContextHandler | Iterable[_ContextHandler] | None = None,
|
|
258
297
|
on_backoff: _ContextHandler | Iterable[_ContextHandler] | None = None,
|
|
259
298
|
on_giveup: _ContextHandler | Iterable[_ContextHandler] | None = None,
|
|
260
299
|
raise_on_giveup: bool = True,
|
|
261
|
-
logger:
|
|
300
|
+
logger: _AnyLoggerOrName | None = "backoff",
|
|
262
301
|
backoff_log_level: int = logging.INFO,
|
|
263
302
|
giveup_log_level: int = logging.ERROR,
|
|
264
303
|
**wait_gen_kwargs: Any,
|
|
265
|
-
) -> Generator[
|
|
304
|
+
) -> Generator[Attempt, None, None]:
|
|
266
305
|
"""Returns a generator of retry attempts, for direct use with a `for` loop.
|
|
267
306
|
|
|
268
307
|
Unlike `on_exception`, this doesn't wrap a whole function; it lets a
|
|
@@ -313,6 +352,7 @@ def retry_context(
|
|
|
313
352
|
passed to wait_gen when it is initialized.
|
|
314
353
|
"""
|
|
315
354
|
logger = _prepare_logger(logger)
|
|
355
|
+
on_try = _config_handlers(on_try)
|
|
316
356
|
on_success = _config_handlers(on_success)
|
|
317
357
|
on_backoff = _config_handlers(
|
|
318
358
|
on_backoff,
|
|
@@ -334,6 +374,7 @@ def retry_context(
|
|
|
334
374
|
max_time=max_time,
|
|
335
375
|
jitter=jitter,
|
|
336
376
|
giveup=giveup,
|
|
377
|
+
on_try=on_try,
|
|
337
378
|
on_success=on_success,
|
|
338
379
|
on_backoff=on_backoff,
|
|
339
380
|
on_giveup=on_giveup,
|
|
@@ -350,15 +391,16 @@ def aretry_context(
|
|
|
350
391
|
max_time: _MaybeCallable[float] | None = None,
|
|
351
392
|
jitter: _Jitterer | None = full_jitter,
|
|
352
393
|
giveup: _Predicate[BaseException] = lambda e: False,
|
|
394
|
+
on_try: _ContextHandler | Iterable[_ContextHandler] | None = None,
|
|
353
395
|
on_success: _ContextHandler | Iterable[_ContextHandler] | None = None,
|
|
354
396
|
on_backoff: _ContextHandler | Iterable[_ContextHandler] | None = None,
|
|
355
397
|
on_giveup: _ContextHandler | Iterable[_ContextHandler] | None = None,
|
|
356
398
|
raise_on_giveup: bool = True,
|
|
357
|
-
logger:
|
|
399
|
+
logger: _AnyLoggerOrName | None = "backoff",
|
|
358
400
|
backoff_log_level: int = logging.INFO,
|
|
359
401
|
giveup_log_level: int = logging.ERROR,
|
|
360
402
|
**wait_gen_kwargs: Any,
|
|
361
|
-
) -> AsyncGenerator[
|
|
403
|
+
) -> AsyncGenerator[Attempt, None]:
|
|
362
404
|
"""Async counterpart to `retry_context`, for use with `async for`.
|
|
363
405
|
|
|
364
406
|
async for attempt in backoff.aretry_context(ValueError, backoff.expo):
|
|
@@ -369,6 +411,7 @@ def aretry_context(
|
|
|
369
411
|
`retry_context` for the full argument reference.
|
|
370
412
|
"""
|
|
371
413
|
logger = _prepare_logger(logger)
|
|
414
|
+
on_try = _config_handlers(on_try)
|
|
372
415
|
on_success = _config_handlers(on_success)
|
|
373
416
|
on_backoff = _config_handlers(
|
|
374
417
|
on_backoff,
|
|
@@ -390,6 +433,7 @@ def aretry_context(
|
|
|
390
433
|
max_time=max_time,
|
|
391
434
|
jitter=jitter,
|
|
392
435
|
giveup=giveup,
|
|
436
|
+
on_try=on_try,
|
|
393
437
|
on_success=on_success,
|
|
394
438
|
on_backoff=on_backoff,
|
|
395
439
|
on_giveup=on_giveup,
|
backoff/_sync.py
CHANGED
|
@@ -2,13 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import functools
|
|
4
4
|
import time
|
|
5
|
-
from typing import TYPE_CHECKING, Any,
|
|
5
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
6
6
|
|
|
7
|
-
from backoff._common import
|
|
7
|
+
from backoff._common import Attempt, _dispatch_handlers, _RetryState
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
import sys
|
|
11
|
-
from collections.abc import Generator, Iterable
|
|
11
|
+
from collections.abc import Callable, Generator, Iterable
|
|
12
|
+
from typing import ParamSpec
|
|
12
13
|
|
|
13
14
|
from backoff._typing import (
|
|
14
15
|
ContextDetails,
|
|
@@ -24,11 +25,6 @@ if TYPE_CHECKING:
|
|
|
24
25
|
_WaitGenerator,
|
|
25
26
|
)
|
|
26
27
|
|
|
27
|
-
if sys.version_info >= (3, 10):
|
|
28
|
-
from typing import ParamSpec
|
|
29
|
-
else:
|
|
30
|
-
from typing_extensions import ParamSpec
|
|
31
|
-
|
|
32
28
|
if sys.version_info >= (3, 11):
|
|
33
29
|
from typing import Unpack
|
|
34
30
|
else:
|
|
@@ -53,9 +49,8 @@ def _call_handlers(
|
|
|
53
49
|
"kwargs": kwargs,
|
|
54
50
|
"tries": tries,
|
|
55
51
|
"elapsed": elapsed,
|
|
52
|
+
**extra,
|
|
56
53
|
}
|
|
57
|
-
# pyrefly: ignore [no-matching-overload]
|
|
58
|
-
details.update(extra)
|
|
59
54
|
for hdlr in hdlrs:
|
|
60
55
|
hdlr(details)
|
|
61
56
|
|
|
@@ -68,6 +63,7 @@ def retry_predicate(
|
|
|
68
63
|
max_tries: _MaybeCallable[int] | None,
|
|
69
64
|
max_time: _MaybeCallable[float] | None,
|
|
70
65
|
jitter: _Jitterer | None,
|
|
66
|
+
on_try: Iterable[_Handler],
|
|
71
67
|
on_success: Iterable[_Handler],
|
|
72
68
|
on_backoff: Iterable[_Handler],
|
|
73
69
|
on_giveup: Iterable[_Handler],
|
|
@@ -83,14 +79,16 @@ def retry_predicate(
|
|
|
83
79
|
)
|
|
84
80
|
while True:
|
|
85
81
|
state.start_attempt()
|
|
86
|
-
ret = target(*args, **kwargs)
|
|
87
82
|
details: _BaseDetails = {
|
|
88
83
|
"target": target,
|
|
89
84
|
"args": args,
|
|
90
85
|
"kwargs": kwargs,
|
|
91
86
|
"tries": state.tries,
|
|
92
|
-
"elapsed": state.
|
|
87
|
+
"elapsed": state.elapsed,
|
|
93
88
|
}
|
|
89
|
+
_call_handlers(on_try, **details)
|
|
90
|
+
ret = target(*args, **kwargs)
|
|
91
|
+
details["elapsed"] = state.record_elapsed()
|
|
94
92
|
|
|
95
93
|
if predicate(ret):
|
|
96
94
|
if state.exhausted():
|
|
@@ -143,6 +141,7 @@ def retry_exception(
|
|
|
143
141
|
max_time: _MaybeCallable[float] | None,
|
|
144
142
|
jitter: _Jitterer | None,
|
|
145
143
|
giveup: _Predicate[Exception],
|
|
144
|
+
on_try: Iterable[_Handler],
|
|
146
145
|
on_success: Iterable[_Handler],
|
|
147
146
|
on_backoff: Iterable[_Handler],
|
|
148
147
|
on_giveup: Iterable[_Handler],
|
|
@@ -160,6 +159,7 @@ def retry_exception(
|
|
|
160
159
|
max_time=max_time,
|
|
161
160
|
jitter=jitter,
|
|
162
161
|
giveup=giveup, # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
|
162
|
+
on_try=_adapt_context_handlers(on_try, target, args, kwargs),
|
|
163
163
|
on_success=_adapt_context_handlers(on_success, target, args, kwargs),
|
|
164
164
|
on_backoff=_adapt_context_handlers(on_backoff, target, args, kwargs),
|
|
165
165
|
on_giveup=_adapt_context_handlers(on_giveup, target, args, kwargs),
|
|
@@ -182,12 +182,13 @@ def retry_context(
|
|
|
182
182
|
max_time: _MaybeCallable[float] | None,
|
|
183
183
|
jitter: _Jitterer | None,
|
|
184
184
|
giveup: _Predicate[BaseException],
|
|
185
|
+
on_try: Iterable[_ContextHandler],
|
|
185
186
|
on_success: Iterable[_ContextHandler],
|
|
186
187
|
on_backoff: Iterable[_ContextHandler],
|
|
187
188
|
on_giveup: Iterable[_ContextHandler],
|
|
188
189
|
raise_on_giveup: bool,
|
|
189
190
|
wait_gen_kwargs: dict[str, Any],
|
|
190
|
-
) -> Generator[
|
|
191
|
+
) -> Generator[Attempt, None, None]:
|
|
191
192
|
state = _RetryState(
|
|
192
193
|
wait_gen,
|
|
193
194
|
wait_gen_kwargs,
|
|
@@ -196,7 +197,8 @@ def retry_context(
|
|
|
196
197
|
)
|
|
197
198
|
while True:
|
|
198
199
|
state.start_attempt()
|
|
199
|
-
attempt =
|
|
200
|
+
attempt = Attempt(exception)
|
|
201
|
+
_dispatch_handlers(handlers=on_try, tries=state.tries, elapsed=state.elapsed)
|
|
200
202
|
yield attempt
|
|
201
203
|
elapsed = state.record_elapsed()
|
|
202
204
|
|
backoff/_typing.py
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from collections.abc import Coroutine, Generator
|
|
5
|
-
from typing import
|
|
6
|
-
Any,
|
|
7
|
-
Callable,
|
|
8
|
-
TypedDict,
|
|
9
|
-
TypeVar,
|
|
10
|
-
Union,
|
|
11
|
-
)
|
|
4
|
+
from collections.abc import Callable, Coroutine, Generator
|
|
5
|
+
from typing import Any, TypeAlias, TypedDict, TypeVar
|
|
12
6
|
|
|
13
7
|
|
|
14
8
|
class _BaseDetails(TypedDict):
|
|
@@ -36,6 +30,7 @@ class _BaseContextDetails(TypedDict):
|
|
|
36
30
|
|
|
37
31
|
class _ContextCallDetails(TypedDict, total=False):
|
|
38
32
|
wait: float # present in the on_backoff handler case
|
|
33
|
+
value: Any # present in the on_predicate decorator case
|
|
39
34
|
exception: Exception # present in the on_giveup handler case
|
|
40
35
|
|
|
41
36
|
|
|
@@ -48,21 +43,16 @@ class ContextDetails(_BaseContextDetails, _ContextCallDetails, total=False):
|
|
|
48
43
|
|
|
49
44
|
T = TypeVar("T")
|
|
50
45
|
|
|
46
|
+
_AnyLogger = logging.Logger | logging.LoggerAdapter # type: ignore[type-arg]
|
|
47
|
+
_AnyLoggerOrName = str | _AnyLogger
|
|
51
48
|
_CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) # ruff:ignore[unused-private-type-var]
|
|
52
|
-
|
|
53
|
-
Callable[[
|
|
54
|
-
Callable[[
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Callable[[ContextDetails], None],
|
|
58
|
-
Callable[[ContextDetails], Coroutine[Any, Any, None]],
|
|
59
|
-
]
|
|
49
|
+
_ContextHandler = (
|
|
50
|
+
Callable[[ContextDetails], None]
|
|
51
|
+
| Callable[[ContextDetails], Coroutine[Any, Any, None]]
|
|
52
|
+
)
|
|
53
|
+
_Handler = Callable[[Details], None] | Callable[[Details], Coroutine[Any, Any, None]]
|
|
60
54
|
_Jitterer = Callable[[float], float]
|
|
61
|
-
_MaybeCallable =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
_Predicate = Union[
|
|
65
|
-
Callable[[T], bool],
|
|
66
|
-
Callable[[T], Coroutine[Any, Any, bool]],
|
|
67
|
-
]
|
|
55
|
+
_MaybeCallable: TypeAlias = T | Callable[[], T] # ruff: ignore[unused-private-type-alias]
|
|
56
|
+
_MaybeTuple: TypeAlias = T | tuple[T, ...] # ruff: ignore[unused-private-type-alias]
|
|
57
|
+
_Predicate = Callable[[T], bool] | Callable[[T], Coroutine[Any, Any, bool]]
|
|
68
58
|
_WaitGenerator = Callable[..., Generator[float, Any, None]]
|
backoff/_wait_gen.py
CHANGED
|
@@ -2,10 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import itertools
|
|
4
4
|
import math
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
|
-
from collections.abc import Generator, Iterable
|
|
8
|
+
from collections.abc import Callable, Generator, Iterable
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def expo(
|
|
@@ -103,6 +103,27 @@ def runtime(*, value: Callable[[Any], float]) -> Generator[float, Any, None]:
|
|
|
103
103
|
"""Generator that is based on parsing the return value or thrown
|
|
104
104
|
exception of the decorated method
|
|
105
105
|
|
|
106
|
+
Useful for honoring a server-specified retry delay, e.g. an HTTP
|
|
107
|
+
`Retry-After` header, rather than a fixed wait sequence:
|
|
108
|
+
|
|
109
|
+
# with on_predicate, `value` receives the return value
|
|
110
|
+
@backoff.on_predicate(
|
|
111
|
+
backoff.runtime,
|
|
112
|
+
predicate=lambda r: r.status_code == 429,
|
|
113
|
+
value=lambda r: int(r.headers.get("Retry-After", 1)),
|
|
114
|
+
)
|
|
115
|
+
def get_page():
|
|
116
|
+
return requests.get(url)
|
|
117
|
+
|
|
118
|
+
# with on_exception, `value` receives the raised exception
|
|
119
|
+
@backoff.on_exception(
|
|
120
|
+
backoff.runtime,
|
|
121
|
+
RetryableError,
|
|
122
|
+
value=lambda e: e.wait_seconds,
|
|
123
|
+
)
|
|
124
|
+
def get_page():
|
|
125
|
+
...
|
|
126
|
+
|
|
106
127
|
Args:
|
|
107
128
|
value: a callable which takes as input the decorated
|
|
108
129
|
function's return value or thrown exception and
|
backoff/types.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: python-backoff
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Function decoration for backoff and retry
|
|
5
5
|
Project-URL: Documentation, https://python-backoff.github.io/backoff
|
|
6
6
|
Project-URL: Repository, https://github.com/python-backoff/backoff
|
|
@@ -15,7 +15,6 @@ Classifier: Natural Language :: English
|
|
|
15
15
|
Classifier: Operating System :: OS Independent
|
|
16
16
|
Classifier: Programming Language :: Python
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -25,7 +24,7 @@ Classifier: Programming Language :: Python :: 3.15
|
|
|
25
24
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
26
|
Classifier: Topic :: Utilities
|
|
28
|
-
Requires-Python: >=3.
|
|
27
|
+
Requires-Python: >=3.10
|
|
29
28
|
Description-Content-Type: text/x-rst
|
|
30
29
|
|
|
31
30
|
backoff
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
backoff/__init__.py,sha256=_AGhwnTFSCdB_oeBA1-X5HPaivH4At8qucG8WQ8kZjI,1039
|
|
2
|
+
backoff/_async.py,sha256=UMAls92zTDOc_W8nmaTIWAEznQgE-bnSCjfoYerVG9Q,9235
|
|
3
|
+
backoff/_common.py,sha256=0ONXWZBiAMSAXw4KN_Cp7mLvhlUPOGV826NHYn6E760,8342
|
|
4
|
+
backoff/_decorator.py,sha256=loMJ2ep7O0KOeXL8vvyMFU9VGK2fKlSsUaZoVCT5Tfs,18110
|
|
5
|
+
backoff/_jitter.py,sha256=wzQHBDNuYg7owbc3fcZLOr5uhhRgqiuncU5rIYOB9lY,766
|
|
6
|
+
backoff/_sync.py,sha256=bUoAkIBUB1TEyvSvHQSulesSjuyvRxD6Lq2SbLcw96o,6710
|
|
7
|
+
backoff/_typing.py,sha256=ZMEhNamBlnXcOqzSCvGqZZ0uc0KVu9-mOKcxccCCPAs,1996
|
|
8
|
+
backoff/_wait_gen.py,sha256=J7-UEqBjxdxvj7LKb5D1ILfPzMrUmCIVX0YsvS107UQ,3752
|
|
9
|
+
backoff/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
backoff/types.py,sha256=obBefroeCaGlPbOQSw1KIKvYl4SKFr8_YvwogvorVnw,97
|
|
11
|
+
python_backoff-3.0.0.dist-info/METADATA,sha256=n9RmkgXbEsR9yRYThMaw0tk7KFaPG6eNIKO_TX5WRBI,14760
|
|
12
|
+
python_backoff-3.0.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
13
|
+
python_backoff-3.0.0.dist-info/licenses/LICENSE,sha256=KmtNX4hNTXob8E6n3xlEzxKzLjWnmobQoHWi0_QPuaw,1077
|
|
14
|
+
python_backoff-3.0.0.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
backoff/__init__.py,sha256=BeZF5I4RDsBoy2C1xNvabGaJ5ez6m8JFteZYElAL_RM,988
|
|
2
|
-
backoff/_async.py,sha256=xx5z-gkSK484W09d2On9vTQvnMbK-KyzVuiZPR67zFA,8936
|
|
3
|
-
backoff/_common.py,sha256=2p0oj1toXysJ7jVxYr6fZ3Q3Pa7CYpje44k116C7il4,8939
|
|
4
|
-
backoff/_decorator.py,sha256=rMTzMgRz7tPI0sIy6tCI9Q0RBStRX7bcgYuiYg9TlKU,16114
|
|
5
|
-
backoff/_jitter.py,sha256=wzQHBDNuYg7owbc3fcZLOr5uhhRgqiuncU5rIYOB9lY,766
|
|
6
|
-
backoff/_sync.py,sha256=iL5JBScW-l93opMSs09lSHMBFnrDBWjpml_Ar12yJME,6510
|
|
7
|
-
backoff/_typing.py,sha256=RkIqYIJwjUlhwg5Z6TIF3HkCIv1oKDAtDGRDZJO0K48,1856
|
|
8
|
-
backoff/_wait_gen.py,sha256=uyDnfEcOzUpLeGqktkVZ2pBr-NKu4K03qYug-VR_KJo,3056
|
|
9
|
-
backoff/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
backoff/types.py,sha256=rOQAfuetqVwfEIuzVvK5D_w97A3wrZ9lQ8HwuJ_uq5s,59
|
|
11
|
-
python_backoff-2.4.0.dist-info/METADATA,sha256=Xek_me-7Sxh1vUE8Tb5NSdeSylTrBIUruOUJwLsQ7XQ,14809
|
|
12
|
-
python_backoff-2.4.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
13
|
-
python_backoff-2.4.0.dist-info/licenses/LICENSE,sha256=KmtNX4hNTXob8E6n3xlEzxKzLjWnmobQoHWi0_QPuaw,1077
|
|
14
|
-
python_backoff-2.4.0.dist-info/RECORD,,
|
|
File without changes
|