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/_sync.py CHANGED
@@ -1,144 +1,238 @@
1
+ from __future__ import annotations
2
+
1
3
  import functools
2
4
  import time
3
-
4
- from backoff._common import _init_wait_gen, _maybe_call, _next_wait
5
-
6
-
7
- def _call_handlers(hdlrs, target, args, kwargs, tries, elapsed, **extra):
8
- details = {
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
- max_tries_value = _maybe_call(max_tries)
36
- max_time_value = _maybe_call(max_time)
37
-
38
- tries = 0
39
- start = time.monotonic()
40
- wait = _init_wait_gen(wait_gen, wait_gen_kwargs)
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
- tries += 1
43
- elapsed = time.monotonic() - start
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": 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
- max_tries_exceeded = tries == max_tries_value
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 = _next_wait(wait, ret, jitter, elapsed, max_time_value)
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
- else:
74
- _call_handlers(on_success, **details, value=ret)
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
- max_tries_value = _maybe_call(max_tries)
100
- max_time_value = _maybe_call(max_time)
101
-
102
- tries = 0
103
- start = time.monotonic()
104
- wait = _init_wait_gen(wait_gen, wait_gen_kwargs)
105
- while True:
106
- tries += 1
107
- elapsed = time.monotonic() - start
108
- details = {
109
- "target": target,
110
- "args": args,
111
- "kwargs": kwargs,
112
- "tries": tries,
113
- "elapsed": elapsed,
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
- try:
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
- time.sleep(seconds)
139
- else:
140
- _call_handlers(on_success, **details)
174
+ return retry
141
175
 
142
- return ret
143
176
 
144
- return retry
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 types import FunctionType
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 _Details(TypedDict):
18
- target: FunctionType
19
- args: Tuple[Any, ...]
20
- kwargs: Dict[str, Any]
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 Details(_Details, total=False):
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
- _MaybeSequence = Union[T, Sequence[T]]
42
- _Predicate = Callable[[T], bool]
43
- _WaitGenerator = Callable[..., Generator[float, None, None]]
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, Generator, Iterable, Optional, Union
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: Optional[float] = None,
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 # type: ignore[misc]
22
- base_n: float = 1
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
- a = factor * base_n
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: Optional[float] = None,
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 # type: ignore[misc]
50
- t = 0
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
- a = initial_value * math.e ** (-t * decay_factor)
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: Optional[int] = None) -> Generator[int, None, None]:
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 # type: ignore[misc]
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
- if max_value is None or a < max_value:
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 # type: ignore[misc]
90
+ yield 0
91
91
 
92
- try:
93
- itr = iter(interval) # type: ignore
94
- except TypeError:
95
- itr = itertools.repeat(interval) # type: ignore
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 # type: ignore[misc]
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.1
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.8
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: # noqa: SIM117
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,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.28.0
2
+ Generator: hatchling 1.31.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -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,,