python-backoff 2.3.1__py3-none-any.whl → 2.4.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 +9 -7
- backoff/_async.py +210 -110
- backoff/_common.py +194 -24
- backoff/_decorator.py +201 -30
- backoff/_sync.py +190 -96
- backoff/_typing.py +40 -15
- backoff/_wait_gen.py +37 -39
- {python_backoff-2.3.1.dist-info → python_backoff-2.4.0.dist-info}/METADATA +3 -5
- python_backoff-2.4.0.dist-info/RECORD +14 -0
- {python_backoff-2.3.1.dist-info → python_backoff-2.4.0.dist-info}/WHEEL +1 -1
- python_backoff-2.3.1.dist-info/RECORD +0 -14
- {python_backoff-2.3.1.dist-info → python_backoff-2.4.0.dist-info}/licenses/LICENSE +0 -0
backoff/_sync.py
CHANGED
|
@@ -1,144 +1,238 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import functools
|
|
2
4
|
import time
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
|
6
|
+
|
|
7
|
+
from backoff._common import _Attempt, _dispatch_handlers, _RetryState
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
import sys
|
|
11
|
+
from collections.abc import Generator, Iterable
|
|
12
|
+
|
|
13
|
+
from backoff._typing import (
|
|
14
|
+
ContextDetails,
|
|
15
|
+
Details,
|
|
16
|
+
_BaseDetails,
|
|
17
|
+
_CallDetails,
|
|
18
|
+
_ContextHandler,
|
|
19
|
+
_Handler,
|
|
20
|
+
_Jitterer,
|
|
21
|
+
_MaybeCallable,
|
|
22
|
+
_MaybeTuple,
|
|
23
|
+
_Predicate,
|
|
24
|
+
_WaitGenerator,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if sys.version_info >= (3, 10):
|
|
28
|
+
from typing import ParamSpec
|
|
29
|
+
else:
|
|
30
|
+
from typing_extensions import ParamSpec
|
|
31
|
+
|
|
32
|
+
if sys.version_info >= (3, 11):
|
|
33
|
+
from typing import Unpack
|
|
34
|
+
else:
|
|
35
|
+
from typing_extensions import Unpack
|
|
36
|
+
|
|
37
|
+
T = TypeVar("T")
|
|
38
|
+
P = ParamSpec("P")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _call_handlers(
|
|
42
|
+
hdlrs: Iterable[_Handler],
|
|
43
|
+
target: Callable[..., Any],
|
|
44
|
+
args: tuple[Any, ...],
|
|
45
|
+
kwargs: dict[str, Any],
|
|
46
|
+
tries: int,
|
|
47
|
+
elapsed: float,
|
|
48
|
+
**extra: Unpack[_CallDetails],
|
|
49
|
+
) -> None:
|
|
50
|
+
details: Details = {
|
|
9
51
|
"target": target,
|
|
10
52
|
"args": args,
|
|
11
53
|
"kwargs": kwargs,
|
|
12
54
|
"tries": tries,
|
|
13
55
|
"elapsed": elapsed,
|
|
14
56
|
}
|
|
57
|
+
# pyrefly: ignore [no-matching-overload]
|
|
15
58
|
details.update(extra)
|
|
16
59
|
for hdlr in hdlrs:
|
|
17
60
|
hdlr(details)
|
|
18
61
|
|
|
19
62
|
|
|
20
63
|
def retry_predicate(
|
|
21
|
-
target,
|
|
22
|
-
wait_gen,
|
|
23
|
-
predicate,
|
|
64
|
+
target: Callable[P, T],
|
|
65
|
+
wait_gen: _WaitGenerator,
|
|
66
|
+
predicate: _Predicate[T],
|
|
24
67
|
*,
|
|
25
|
-
max_tries,
|
|
26
|
-
max_time,
|
|
27
|
-
jitter,
|
|
28
|
-
on_success,
|
|
29
|
-
on_backoff,
|
|
30
|
-
on_giveup,
|
|
31
|
-
wait_gen_kwargs,
|
|
32
|
-
):
|
|
68
|
+
max_tries: _MaybeCallable[int] | None,
|
|
69
|
+
max_time: _MaybeCallable[float] | None,
|
|
70
|
+
jitter: _Jitterer | None,
|
|
71
|
+
on_success: Iterable[_Handler],
|
|
72
|
+
on_backoff: Iterable[_Handler],
|
|
73
|
+
on_giveup: Iterable[_Handler],
|
|
74
|
+
wait_gen_kwargs: dict[str, Any],
|
|
75
|
+
) -> Callable[P, T]:
|
|
33
76
|
@functools.wraps(target)
|
|
34
|
-
def retry(*args, **kwargs):
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
77
|
+
def retry(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
78
|
+
state = _RetryState(
|
|
79
|
+
wait_gen,
|
|
80
|
+
wait_gen_kwargs,
|
|
81
|
+
max_tries=max_tries,
|
|
82
|
+
max_time=max_time,
|
|
83
|
+
)
|
|
41
84
|
while True:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
details = {
|
|
85
|
+
state.start_attempt()
|
|
86
|
+
ret = target(*args, **kwargs)
|
|
87
|
+
details: _BaseDetails = {
|
|
45
88
|
"target": target,
|
|
46
89
|
"args": args,
|
|
47
90
|
"kwargs": kwargs,
|
|
48
|
-
"tries": tries,
|
|
49
|
-
"elapsed":
|
|
91
|
+
"tries": state.tries,
|
|
92
|
+
"elapsed": state.record_elapsed(),
|
|
50
93
|
}
|
|
51
94
|
|
|
52
|
-
ret = target(*args, **kwargs)
|
|
53
95
|
if predicate(ret):
|
|
54
|
-
|
|
55
|
-
max_time_exceeded = (
|
|
56
|
-
max_time_value is not None and elapsed >= max_time_value
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
if max_tries_exceeded or max_time_exceeded:
|
|
96
|
+
if state.exhausted():
|
|
60
97
|
_call_handlers(on_giveup, **details, value=ret)
|
|
61
98
|
break
|
|
62
99
|
|
|
63
100
|
try:
|
|
64
|
-
seconds =
|
|
101
|
+
seconds = state.next_wait(ret, jitter)
|
|
65
102
|
except StopIteration:
|
|
66
|
-
_call_handlers(on_giveup, **details)
|
|
103
|
+
_call_handlers(on_giveup, **details, value=ret)
|
|
67
104
|
break
|
|
68
105
|
|
|
69
106
|
_call_handlers(on_backoff, **details, value=ret, wait=seconds)
|
|
70
107
|
|
|
71
108
|
time.sleep(seconds)
|
|
72
109
|
continue
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
break
|
|
110
|
+
_call_handlers(on_success, **details, value=ret)
|
|
111
|
+
break
|
|
76
112
|
|
|
77
113
|
return ret
|
|
78
114
|
|
|
79
115
|
return retry
|
|
80
116
|
|
|
81
117
|
|
|
118
|
+
def _adapt_context_handlers(
|
|
119
|
+
handlers: Iterable[_Handler],
|
|
120
|
+
target: Callable[..., Any],
|
|
121
|
+
args: tuple[Any, ...],
|
|
122
|
+
kwargs: dict[str, Any],
|
|
123
|
+
) -> list[_ContextHandler]:
|
|
124
|
+
def adapted(details: ContextDetails) -> None:
|
|
125
|
+
full_details: Details = {
|
|
126
|
+
"target": target,
|
|
127
|
+
"args": args,
|
|
128
|
+
"kwargs": kwargs,
|
|
129
|
+
**details,
|
|
130
|
+
}
|
|
131
|
+
for hdlr in handlers:
|
|
132
|
+
hdlr(full_details)
|
|
133
|
+
|
|
134
|
+
return [adapted]
|
|
135
|
+
|
|
136
|
+
|
|
82
137
|
def retry_exception(
|
|
83
|
-
target,
|
|
84
|
-
wait_gen,
|
|
85
|
-
exception,
|
|
138
|
+
target: Callable[P, T],
|
|
139
|
+
wait_gen: _WaitGenerator,
|
|
140
|
+
exception: _MaybeTuple[type[Exception]],
|
|
86
141
|
*,
|
|
87
|
-
max_tries,
|
|
88
|
-
max_time,
|
|
89
|
-
jitter,
|
|
90
|
-
giveup,
|
|
91
|
-
on_success,
|
|
92
|
-
on_backoff,
|
|
93
|
-
on_giveup,
|
|
94
|
-
raise_on_giveup,
|
|
95
|
-
wait_gen_kwargs,
|
|
96
|
-
):
|
|
142
|
+
max_tries: _MaybeCallable[int] | None,
|
|
143
|
+
max_time: _MaybeCallable[float] | None,
|
|
144
|
+
jitter: _Jitterer | None,
|
|
145
|
+
giveup: _Predicate[Exception],
|
|
146
|
+
on_success: Iterable[_Handler],
|
|
147
|
+
on_backoff: Iterable[_Handler],
|
|
148
|
+
on_giveup: Iterable[_Handler],
|
|
149
|
+
raise_on_giveup: bool,
|
|
150
|
+
wait_gen_kwargs: dict[str, Any],
|
|
151
|
+
) -> Callable[P, T]:
|
|
97
152
|
@functools.wraps(target)
|
|
98
|
-
def retry(*args, **kwargs):
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
try:
|
|
153
|
+
def retry(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
154
|
+
ret: T = None # type: ignore[assignment] # ty:ignore[invalid-assignment]
|
|
155
|
+
|
|
156
|
+
for attempt in retry_context(
|
|
157
|
+
exception,
|
|
158
|
+
wait_gen,
|
|
159
|
+
max_tries=max_tries,
|
|
160
|
+
max_time=max_time,
|
|
161
|
+
jitter=jitter,
|
|
162
|
+
giveup=giveup, # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
|
163
|
+
on_success=_adapt_context_handlers(on_success, target, args, kwargs),
|
|
164
|
+
on_backoff=_adapt_context_handlers(on_backoff, target, args, kwargs),
|
|
165
|
+
on_giveup=_adapt_context_handlers(on_giveup, target, args, kwargs),
|
|
166
|
+
raise_on_giveup=raise_on_giveup,
|
|
167
|
+
wait_gen_kwargs=wait_gen_kwargs,
|
|
168
|
+
):
|
|
169
|
+
with attempt:
|
|
117
170
|
ret = target(*args, **kwargs)
|
|
118
|
-
except exception as e:
|
|
119
|
-
max_tries_exceeded = tries == max_tries_value
|
|
120
|
-
max_time_exceeded = (
|
|
121
|
-
max_time_value is not None and elapsed >= max_time_value
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
if giveup(e) or max_tries_exceeded or max_time_exceeded:
|
|
125
|
-
_call_handlers(on_giveup, **details, exception=e)
|
|
126
|
-
if raise_on_giveup:
|
|
127
|
-
raise
|
|
128
|
-
return None
|
|
129
171
|
|
|
130
|
-
|
|
131
|
-
seconds = _next_wait(wait, e, jitter, elapsed, max_time_value)
|
|
132
|
-
except StopIteration:
|
|
133
|
-
_call_handlers(on_giveup, **details, exception=e)
|
|
134
|
-
raise e
|
|
135
|
-
|
|
136
|
-
_call_handlers(on_backoff, **details, wait=seconds, exception=e)
|
|
172
|
+
return ret
|
|
137
173
|
|
|
138
|
-
|
|
139
|
-
else:
|
|
140
|
-
_call_handlers(on_success, **details)
|
|
174
|
+
return retry
|
|
141
175
|
|
|
142
|
-
return ret
|
|
143
176
|
|
|
144
|
-
|
|
177
|
+
def retry_context(
|
|
178
|
+
exception: _MaybeTuple[type[Exception]],
|
|
179
|
+
wait_gen: _WaitGenerator,
|
|
180
|
+
*,
|
|
181
|
+
max_tries: _MaybeCallable[int] | None,
|
|
182
|
+
max_time: _MaybeCallable[float] | None,
|
|
183
|
+
jitter: _Jitterer | None,
|
|
184
|
+
giveup: _Predicate[BaseException],
|
|
185
|
+
on_success: Iterable[_ContextHandler],
|
|
186
|
+
on_backoff: Iterable[_ContextHandler],
|
|
187
|
+
on_giveup: Iterable[_ContextHandler],
|
|
188
|
+
raise_on_giveup: bool,
|
|
189
|
+
wait_gen_kwargs: dict[str, Any],
|
|
190
|
+
) -> Generator[_Attempt, None, None]:
|
|
191
|
+
state = _RetryState(
|
|
192
|
+
wait_gen,
|
|
193
|
+
wait_gen_kwargs,
|
|
194
|
+
max_tries=max_tries,
|
|
195
|
+
max_time=max_time,
|
|
196
|
+
)
|
|
197
|
+
while True:
|
|
198
|
+
state.start_attempt()
|
|
199
|
+
attempt = _Attempt(exception)
|
|
200
|
+
yield attempt
|
|
201
|
+
elapsed = state.record_elapsed()
|
|
202
|
+
|
|
203
|
+
exc = attempt.exception
|
|
204
|
+
if exc is None:
|
|
205
|
+
_dispatch_handlers(on_success, tries=state.tries, elapsed=elapsed)
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
if giveup(exc) or state.exhausted():
|
|
209
|
+
_dispatch_handlers(
|
|
210
|
+
on_giveup,
|
|
211
|
+
tries=state.tries,
|
|
212
|
+
elapsed=elapsed,
|
|
213
|
+
exception=exc,
|
|
214
|
+
)
|
|
215
|
+
if raise_on_giveup:
|
|
216
|
+
raise exc
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
seconds = state.next_wait(exc, jitter)
|
|
221
|
+
except StopIteration:
|
|
222
|
+
_dispatch_handlers(
|
|
223
|
+
on_giveup,
|
|
224
|
+
tries=state.tries,
|
|
225
|
+
elapsed=elapsed,
|
|
226
|
+
exception=exc,
|
|
227
|
+
)
|
|
228
|
+
raise exc from None
|
|
229
|
+
|
|
230
|
+
_dispatch_handlers(
|
|
231
|
+
on_backoff,
|
|
232
|
+
tries=state.tries,
|
|
233
|
+
elapsed=elapsed,
|
|
234
|
+
wait=seconds,
|
|
235
|
+
exception=exc,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
time.sleep(seconds)
|
backoff/_typing.py
CHANGED
|
@@ -1,43 +1,68 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
|
-
from
|
|
4
|
+
from collections.abc import Coroutine, Generator
|
|
3
5
|
from typing import (
|
|
4
6
|
Any,
|
|
5
7
|
Callable,
|
|
6
|
-
Coroutine,
|
|
7
|
-
Dict,
|
|
8
|
-
Generator,
|
|
9
|
-
Sequence,
|
|
10
|
-
Tuple,
|
|
11
8
|
TypedDict,
|
|
12
9
|
TypeVar,
|
|
13
10
|
Union,
|
|
14
11
|
)
|
|
15
12
|
|
|
16
13
|
|
|
17
|
-
class
|
|
18
|
-
target:
|
|
19
|
-
args:
|
|
20
|
-
kwargs:
|
|
14
|
+
class _BaseDetails(TypedDict):
|
|
15
|
+
target: Callable[..., Any]
|
|
16
|
+
args: tuple[Any, ...]
|
|
17
|
+
kwargs: dict[str, Any]
|
|
21
18
|
tries: int
|
|
22
19
|
elapsed: float
|
|
23
20
|
|
|
24
21
|
|
|
25
|
-
class
|
|
22
|
+
class _CallDetails(TypedDict, total=False):
|
|
26
23
|
wait: float # present in the on_backoff handler case for either decorator
|
|
27
24
|
value: Any # present in the on_predicate decorator case
|
|
28
25
|
exception: Exception # present in the on_exception decorator case
|
|
29
26
|
|
|
30
27
|
|
|
28
|
+
class Details(_BaseDetails, _CallDetails, total=False):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _BaseContextDetails(TypedDict):
|
|
33
|
+
tries: int
|
|
34
|
+
elapsed: float
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _ContextCallDetails(TypedDict, total=False):
|
|
38
|
+
wait: float # present in the on_backoff handler case
|
|
39
|
+
exception: Exception # present in the on_giveup handler case
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ContextDetails(_BaseContextDetails, _ContextCallDetails, total=False):
|
|
43
|
+
"""Details passed to handlers registered with `retry_context`/`aretry_context`.
|
|
44
|
+
|
|
45
|
+
Unlike `Details`, there's no wrapped callable, so no `target`/`args`/`kwargs`.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
|
|
31
49
|
T = TypeVar("T")
|
|
32
50
|
|
|
33
|
-
_CallableT = TypeVar("_CallableT", bound=Callable[..., Any])
|
|
51
|
+
_CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) # ruff:ignore[unused-private-type-var]
|
|
34
52
|
_Handler = Union[
|
|
35
53
|
Callable[[Details], None],
|
|
36
54
|
Callable[[Details], Coroutine[Any, Any, None]],
|
|
37
55
|
]
|
|
56
|
+
_ContextHandler = Union[
|
|
57
|
+
Callable[[ContextDetails], None],
|
|
58
|
+
Callable[[ContextDetails], Coroutine[Any, Any, None]],
|
|
59
|
+
]
|
|
38
60
|
_Jitterer = Callable[[float], float]
|
|
39
61
|
_MaybeCallable = Union[T, Callable[[], T]]
|
|
40
62
|
_MaybeLogger = Union[str, logging.Logger, logging.LoggerAdapter, None]
|
|
41
|
-
|
|
42
|
-
_Predicate =
|
|
43
|
-
|
|
63
|
+
_MaybeTuple = Union[T, tuple[T, ...]]
|
|
64
|
+
_Predicate = Union[
|
|
65
|
+
Callable[[T], bool],
|
|
66
|
+
Callable[[T], Coroutine[Any, Any, bool]],
|
|
67
|
+
]
|
|
68
|
+
_WaitGenerator = Callable[..., Generator[float, Any, None]]
|
backoff/_wait_gen.py
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import itertools
|
|
2
4
|
import math
|
|
3
|
-
from typing import Any, Callable
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Generator, Iterable
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
def expo(
|
|
7
12
|
base: float = 2,
|
|
8
13
|
factor: float = 1,
|
|
9
|
-
max_value:
|
|
14
|
+
max_value: float | None = None,
|
|
10
15
|
) -> Generator[float, Any, None]:
|
|
11
16
|
"""Generator for exponential decay.
|
|
12
17
|
|
|
@@ -18,21 +23,20 @@ def expo(
|
|
|
18
23
|
of max_value will forever after be yielded.
|
|
19
24
|
"""
|
|
20
25
|
# Advance past initial .send() call
|
|
21
|
-
yield
|
|
22
|
-
|
|
26
|
+
yield 0
|
|
27
|
+
|
|
28
|
+
a = factor
|
|
29
|
+
while max_value is None or a < max_value:
|
|
30
|
+
yield a
|
|
31
|
+
a *= base
|
|
23
32
|
while True:
|
|
24
|
-
|
|
25
|
-
if max_value is None or a < max_value:
|
|
26
|
-
yield a
|
|
27
|
-
base_n *= base
|
|
28
|
-
else:
|
|
29
|
-
yield max_value
|
|
33
|
+
yield max_value
|
|
30
34
|
|
|
31
35
|
|
|
32
36
|
def decay(
|
|
33
37
|
initial_value: float = 1,
|
|
34
38
|
decay_factor: float = 1,
|
|
35
|
-
min_value:
|
|
39
|
+
min_value: float | None = None,
|
|
36
40
|
) -> Generator[float, Any, None]:
|
|
37
41
|
"""Generator for exponential decay[1]:
|
|
38
42
|
|
|
@@ -46,18 +50,17 @@ def decay(
|
|
|
46
50
|
[1] https://en.wikipedia.org/wiki/Exponential_decay
|
|
47
51
|
"""
|
|
48
52
|
# Advance past initial .send() call
|
|
49
|
-
yield
|
|
50
|
-
|
|
53
|
+
yield 0
|
|
54
|
+
a = initial_value
|
|
55
|
+
min_value = min_value or 0.0
|
|
56
|
+
while a > min_value:
|
|
57
|
+
yield a
|
|
58
|
+
a *= math.exp(-decay_factor)
|
|
51
59
|
while True:
|
|
52
|
-
|
|
53
|
-
if min_value is None or a > min_value:
|
|
54
|
-
yield a
|
|
55
|
-
t += 1
|
|
56
|
-
else:
|
|
57
|
-
yield min_value
|
|
60
|
+
yield min_value
|
|
58
61
|
|
|
59
62
|
|
|
60
|
-
def fibo(max_value:
|
|
63
|
+
def fibo(max_value: int | None = None) -> Generator[int, Any, None]:
|
|
61
64
|
"""Generator for fibonaccial decay.
|
|
62
65
|
|
|
63
66
|
Args:
|
|
@@ -66,42 +69,37 @@ def fibo(max_value: Optional[int] = None) -> Generator[int, None, None]:
|
|
|
66
69
|
of max_value will forever after be yielded.
|
|
67
70
|
"""
|
|
68
71
|
# Advance past initial .send() call
|
|
69
|
-
yield
|
|
72
|
+
yield 0
|
|
70
73
|
|
|
71
74
|
a = 1
|
|
72
75
|
b = 1
|
|
76
|
+
while max_value is None or a < max_value:
|
|
77
|
+
yield a
|
|
78
|
+
a, b = b, a + b
|
|
73
79
|
while True:
|
|
74
|
-
|
|
75
|
-
yield a
|
|
76
|
-
a, b = b, a + b
|
|
77
|
-
else:
|
|
78
|
-
yield max_value
|
|
80
|
+
yield max_value
|
|
79
81
|
|
|
80
82
|
|
|
81
|
-
def constant(
|
|
82
|
-
interval: Union[int, Iterable[float]] = 1,
|
|
83
|
-
) -> Generator[float, None, None]:
|
|
83
|
+
def constant(interval: float | Iterable[float] = 1) -> Generator[float, Any, None]:
|
|
84
84
|
"""Generator for constant intervals.
|
|
85
85
|
|
|
86
86
|
Args:
|
|
87
87
|
interval: A constant value to yield or an iterable of such values.
|
|
88
88
|
"""
|
|
89
89
|
# Advance past initial .send() call
|
|
90
|
-
yield
|
|
90
|
+
yield 0
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
itr = (
|
|
93
|
+
itertools.repeat(interval)
|
|
94
|
+
if isinstance(interval, (int, float))
|
|
95
|
+
else iter(interval)
|
|
96
|
+
)
|
|
96
97
|
|
|
97
98
|
for val in itr:
|
|
98
99
|
yield val
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
def runtime(
|
|
102
|
-
*,
|
|
103
|
-
value: Callable[[Any], float],
|
|
104
|
-
) -> Generator[float, None, None]:
|
|
102
|
+
def runtime(*, value: Callable[[Any], float]) -> Generator[float, Any, None]:
|
|
105
103
|
"""Generator that is based on parsing the return value or thrown
|
|
106
104
|
exception of the decorated method
|
|
107
105
|
|
|
@@ -110,6 +108,6 @@ def runtime(
|
|
|
110
108
|
function's return value or thrown exception and
|
|
111
109
|
determines how long to wait
|
|
112
110
|
"""
|
|
113
|
-
ret_or_exc = yield
|
|
111
|
+
ret_or_exc = yield 0
|
|
114
112
|
while True:
|
|
115
113
|
ret_or_exc = yield value(ret_or_exc)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-backoff
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.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
|
|
@@ -11,12 +11,10 @@ License-File: LICENSE
|
|
|
11
11
|
Keywords: backoff,decorators,retry
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
14
|
Classifier: Natural Language :: English
|
|
16
15
|
Classifier: Operating System :: OS Independent
|
|
17
16
|
Classifier: Programming Language :: Python
|
|
18
17
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -27,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.15
|
|
|
27
25
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
28
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
27
|
Classifier: Topic :: Utilities
|
|
30
|
-
Requires-Python: >=3.
|
|
28
|
+
Requires-Python: >=3.9
|
|
31
29
|
Description-Content-Type: text/x-rst
|
|
32
30
|
|
|
33
31
|
backoff
|
|
@@ -406,7 +404,7 @@ asynchronous HTTP client/server library.
|
|
|
406
404
|
max_time=60,
|
|
407
405
|
)
|
|
408
406
|
async def get_url(url):
|
|
409
|
-
async with aiohttp.ClientSession(raise_for_status=True) as session: #
|
|
407
|
+
async with aiohttp.ClientSession(raise_for_status=True) as session: # ruff:ignore[multiple-with-statements]
|
|
410
408
|
async with session.get(url) as response:
|
|
411
409
|
return await response.text()
|
|
412
410
|
|
|
@@ -0,0 +1,14 @@
|
|
|
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,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
backoff/__init__.py,sha256=MS4ieVYpjBh-feDqzJHYJy8mjnuEEaprTWWuXgJI-1A,914
|
|
2
|
-
backoff/_async.py,sha256=Ehv3H3jMF9JylzZzHTLSLwK_s0PoJa_1jeGo3qtpcOY,6370
|
|
3
|
-
backoff/_common.py,sha256=Q8ZB8mCMI76WLii8JcrfoSZc11WRqu7ci_mpG1y-j9Y,3460
|
|
4
|
-
backoff/_decorator.py,sha256=61iXFb5jN2mQ9-1mtRPi_gysJwuyhkKrbDVNWH8GDxo,9483
|
|
5
|
-
backoff/_jitter.py,sha256=wzQHBDNuYg7owbc3fcZLOr5uhhRgqiuncU5rIYOB9lY,766
|
|
6
|
-
backoff/_sync.py,sha256=7W0aTlntrL237rfhy2yaFklZheQoYxBTD5F_4TUarQI,3867
|
|
7
|
-
backoff/_typing.py,sha256=hO065un_HjJEsEa_OgW66JZRyxVf-BsJ9HrswIwW3fA,1056
|
|
8
|
-
backoff/_wait_gen.py,sha256=ZTZfs4WiI295EvOJeJYPIUAABQ0WBAKcvUtg0_ZId8k,3244
|
|
9
|
-
backoff/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
backoff/types.py,sha256=rOQAfuetqVwfEIuzVvK5D_w97A3wrZ9lQ8HwuJ_uq5s,59
|
|
11
|
-
python_backoff-2.3.1.dist-info/METADATA,sha256=o7ltpQ3fk4s05tUCVIeUgZVzGFfigmw06jNoz6HM6RI,14885
|
|
12
|
-
python_backoff-2.3.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
13
|
-
python_backoff-2.3.1.dist-info/licenses/LICENSE,sha256=KmtNX4hNTXob8E6n3xlEzxKzLjWnmobQoHWi0_QPuaw,1077
|
|
14
|
-
python_backoff-2.3.1.dist-info/RECORD,,
|
|
File without changes
|