tenacity 8.4.0__py3-none-any.whl → 8.4.2__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.
tenacity/__init__.py CHANGED
@@ -329,13 +329,19 @@ class BaseRetrying(ABC):
329
329
  f, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")
330
330
  )
331
331
  def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any:
332
- return self(f, *args, **kw)
332
+ # Always create a copy to prevent overwriting the local contexts when
333
+ # calling the same wrapped functions multiple times in the same stack
334
+ copy = self.copy()
335
+ wrapped_f.statistics = copy.statistics # type: ignore[attr-defined]
336
+ return copy(f, *args, **kw)
333
337
 
334
338
  def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:
335
339
  return self.copy(*args, **kwargs).wraps(f)
336
340
 
337
- wrapped_f.retry = self # type: ignore[attr-defined]
341
+ # Preserve attributes
342
+ wrapped_f.retry = wrapped_f # type: ignore[attr-defined]
338
343
  wrapped_f.retry_with = retry_with # type: ignore[attr-defined]
344
+ wrapped_f.statistics = {} # type: ignore[attr-defined]
339
345
 
340
346
  return wrapped_f # type: ignore[return-value]
341
347
 
@@ -0,0 +1,206 @@
1
+ # Copyright 2016 Étienne Bersac
2
+ # Copyright 2016 Julien Danjou
3
+ # Copyright 2016 Joshua Harlow
4
+ # Copyright 2013-2014 Ray Holder
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ import functools
19
+ import sys
20
+ import typing as t
21
+
22
+ import tenacity
23
+ from tenacity import AttemptManager
24
+ from tenacity import BaseRetrying
25
+ from tenacity import DoAttempt
26
+ from tenacity import DoSleep
27
+ from tenacity import RetryCallState
28
+ from tenacity import RetryError
29
+ from tenacity import after_nothing
30
+ from tenacity import before_nothing
31
+ from tenacity import _utils
32
+
33
+ # Import all built-in retry strategies for easier usage.
34
+ from .retry import RetryBaseT
35
+ from .retry import retry_all # noqa
36
+ from .retry import retry_any # noqa
37
+ from .retry import retry_if_exception # noqa
38
+ from .retry import retry_if_result # noqa
39
+ from ..retry import RetryBaseT as SyncRetryBaseT
40
+
41
+ if t.TYPE_CHECKING:
42
+ from tenacity.stop import StopBaseT
43
+ from tenacity.wait import WaitBaseT
44
+
45
+ WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
46
+ WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Awaitable[t.Any]])
47
+
48
+
49
+ def _portable_async_sleep(seconds: float) -> t.Awaitable[None]:
50
+ # If trio is already imported, then importing it is cheap.
51
+ # If trio isn't already imported, then it's definitely not running, so we
52
+ # can skip further checks.
53
+ if "trio" in sys.modules:
54
+ # If trio is available, then sniffio is too
55
+ import trio
56
+ import sniffio
57
+
58
+ if sniffio.current_async_library() == "trio":
59
+ return trio.sleep(seconds)
60
+ # Otherwise, assume asyncio
61
+ # Lazy import asyncio as it's expensive (responsible for 25-50% of total import overhead).
62
+ import asyncio
63
+
64
+ return asyncio.sleep(seconds)
65
+
66
+
67
+ class AsyncRetrying(BaseRetrying):
68
+ def __init__(
69
+ self,
70
+ sleep: t.Callable[
71
+ [t.Union[int, float]], t.Union[None, t.Awaitable[None]]
72
+ ] = _portable_async_sleep,
73
+ stop: "StopBaseT" = tenacity.stop.stop_never,
74
+ wait: "WaitBaseT" = tenacity.wait.wait_none(),
75
+ retry: "t.Union[SyncRetryBaseT, RetryBaseT]" = tenacity.retry_if_exception_type(),
76
+ before: t.Callable[
77
+ ["RetryCallState"], t.Union[None, t.Awaitable[None]]
78
+ ] = before_nothing,
79
+ after: t.Callable[
80
+ ["RetryCallState"], t.Union[None, t.Awaitable[None]]
81
+ ] = after_nothing,
82
+ before_sleep: t.Optional[
83
+ t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]]
84
+ ] = None,
85
+ reraise: bool = False,
86
+ retry_error_cls: t.Type["RetryError"] = RetryError,
87
+ retry_error_callback: t.Optional[
88
+ t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]]
89
+ ] = None,
90
+ ) -> None:
91
+ super().__init__(
92
+ sleep=sleep, # type: ignore[arg-type]
93
+ stop=stop,
94
+ wait=wait,
95
+ retry=retry, # type: ignore[arg-type]
96
+ before=before, # type: ignore[arg-type]
97
+ after=after, # type: ignore[arg-type]
98
+ before_sleep=before_sleep, # type: ignore[arg-type]
99
+ reraise=reraise,
100
+ retry_error_cls=retry_error_cls,
101
+ retry_error_callback=retry_error_callback,
102
+ )
103
+
104
+ async def __call__( # type: ignore[override]
105
+ self, fn: WrappedFn, *args: t.Any, **kwargs: t.Any
106
+ ) -> WrappedFnReturnT:
107
+ self.begin()
108
+
109
+ retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
110
+ while True:
111
+ do = await self.iter(retry_state=retry_state)
112
+ if isinstance(do, DoAttempt):
113
+ try:
114
+ result = await fn(*args, **kwargs)
115
+ except BaseException: # noqa: B902
116
+ retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
117
+ else:
118
+ retry_state.set_result(result)
119
+ elif isinstance(do, DoSleep):
120
+ retry_state.prepare_for_next_attempt()
121
+ await self.sleep(do) # type: ignore[misc]
122
+ else:
123
+ return do # type: ignore[no-any-return]
124
+
125
+ def _add_action_func(self, fn: t.Callable[..., t.Any]) -> None:
126
+ self.iter_state.actions.append(_utils.wrap_to_async_func(fn))
127
+
128
+ async def _run_retry(self, retry_state: "RetryCallState") -> None: # type: ignore[override]
129
+ self.iter_state.retry_run_result = await _utils.wrap_to_async_func(self.retry)(
130
+ retry_state
131
+ )
132
+
133
+ async def _run_wait(self, retry_state: "RetryCallState") -> None: # type: ignore[override]
134
+ if self.wait:
135
+ sleep = await _utils.wrap_to_async_func(self.wait)(retry_state)
136
+ else:
137
+ sleep = 0.0
138
+
139
+ retry_state.upcoming_sleep = sleep
140
+
141
+ async def _run_stop(self, retry_state: "RetryCallState") -> None: # type: ignore[override]
142
+ self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start
143
+ self.iter_state.stop_run_result = await _utils.wrap_to_async_func(self.stop)(
144
+ retry_state
145
+ )
146
+
147
+ async def iter(
148
+ self, retry_state: "RetryCallState"
149
+ ) -> t.Union[DoAttempt, DoSleep, t.Any]: # noqa: A003
150
+ self._begin_iter(retry_state)
151
+ result = None
152
+ for action in self.iter_state.actions:
153
+ result = await action(retry_state)
154
+ return result
155
+
156
+ def __iter__(self) -> t.Generator[AttemptManager, None, None]:
157
+ raise TypeError("AsyncRetrying object is not iterable")
158
+
159
+ def __aiter__(self) -> "AsyncRetrying":
160
+ self.begin()
161
+ self._retry_state = RetryCallState(self, fn=None, args=(), kwargs={})
162
+ return self
163
+
164
+ async def __anext__(self) -> AttemptManager:
165
+ while True:
166
+ do = await self.iter(retry_state=self._retry_state)
167
+ if do is None:
168
+ raise StopAsyncIteration
169
+ elif isinstance(do, DoAttempt):
170
+ return AttemptManager(retry_state=self._retry_state)
171
+ elif isinstance(do, DoSleep):
172
+ self._retry_state.prepare_for_next_attempt()
173
+ await self.sleep(do) # type: ignore[misc]
174
+ else:
175
+ raise StopAsyncIteration
176
+
177
+ def wraps(self, fn: WrappedFn) -> WrappedFn:
178
+ wrapped = super().wraps(fn)
179
+ # Ensure wrapper is recognized as a coroutine function.
180
+
181
+ @functools.wraps(
182
+ fn, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")
183
+ )
184
+ async def async_wrapped(*args: t.Any, **kwargs: t.Any) -> t.Any:
185
+ # Always create a copy to prevent overwriting the local contexts when
186
+ # calling the same wrapped functions multiple times in the same stack
187
+ copy = self.copy()
188
+ async_wrapped.statistics = copy.statistics # type: ignore[attr-defined]
189
+ return await copy(fn, *args, **kwargs)
190
+
191
+ # Preserve attributes
192
+ async_wrapped.retry = async_wrapped # type: ignore[attr-defined]
193
+ async_wrapped.retry_with = wrapped.retry_with # type: ignore[attr-defined]
194
+ async_wrapped.statistics = {} # type: ignore[attr-defined]
195
+
196
+ return async_wrapped # type: ignore[return-value]
197
+
198
+
199
+ __all__ = [
200
+ "retry_all",
201
+ "retry_any",
202
+ "retry_if_exception",
203
+ "retry_if_result",
204
+ "WrappedFn",
205
+ "AsyncRetrying",
206
+ ]
@@ -0,0 +1,125 @@
1
+ # Copyright 2016–2021 Julien Danjou
2
+ # Copyright 2016 Joshua Harlow
3
+ # Copyright 2013-2014 Ray Holder
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ import abc
17
+ import typing
18
+
19
+ from tenacity import _utils
20
+ from tenacity import retry_base
21
+
22
+ if typing.TYPE_CHECKING:
23
+ from tenacity import RetryCallState
24
+
25
+
26
+ class async_retry_base(retry_base):
27
+ """Abstract base class for async retry strategies."""
28
+
29
+ @abc.abstractmethod
30
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
31
+ pass
32
+
33
+ def __and__( # type: ignore[override]
34
+ self, other: "typing.Union[retry_base, async_retry_base]"
35
+ ) -> "retry_all":
36
+ return retry_all(self, other)
37
+
38
+ def __rand__( # type: ignore[misc,override]
39
+ self, other: "typing.Union[retry_base, async_retry_base]"
40
+ ) -> "retry_all":
41
+ return retry_all(other, self)
42
+
43
+ def __or__( # type: ignore[override]
44
+ self, other: "typing.Union[retry_base, async_retry_base]"
45
+ ) -> "retry_any":
46
+ return retry_any(self, other)
47
+
48
+ def __ror__( # type: ignore[misc,override]
49
+ self, other: "typing.Union[retry_base, async_retry_base]"
50
+ ) -> "retry_any":
51
+ return retry_any(other, self)
52
+
53
+
54
+ RetryBaseT = typing.Union[
55
+ async_retry_base, typing.Callable[["RetryCallState"], typing.Awaitable[bool]]
56
+ ]
57
+
58
+
59
+ class retry_if_exception(async_retry_base):
60
+ """Retry strategy that retries if an exception verifies a predicate."""
61
+
62
+ def __init__(
63
+ self, predicate: typing.Callable[[BaseException], typing.Awaitable[bool]]
64
+ ) -> None:
65
+ self.predicate = predicate
66
+
67
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
68
+ if retry_state.outcome is None:
69
+ raise RuntimeError("__call__() called before outcome was set")
70
+
71
+ if retry_state.outcome.failed:
72
+ exception = retry_state.outcome.exception()
73
+ if exception is None:
74
+ raise RuntimeError("outcome failed but the exception is None")
75
+ return await self.predicate(exception)
76
+ else:
77
+ return False
78
+
79
+
80
+ class retry_if_result(async_retry_base):
81
+ """Retries if the result verifies a predicate."""
82
+
83
+ def __init__(
84
+ self, predicate: typing.Callable[[typing.Any], typing.Awaitable[bool]]
85
+ ) -> None:
86
+ self.predicate = predicate
87
+
88
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
89
+ if retry_state.outcome is None:
90
+ raise RuntimeError("__call__() called before outcome was set")
91
+
92
+ if not retry_state.outcome.failed:
93
+ return await self.predicate(retry_state.outcome.result())
94
+ else:
95
+ return False
96
+
97
+
98
+ class retry_any(async_retry_base):
99
+ """Retries if any of the retries condition is valid."""
100
+
101
+ def __init__(self, *retries: typing.Union[retry_base, async_retry_base]) -> None:
102
+ self.retries = retries
103
+
104
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
105
+ result = False
106
+ for r in self.retries:
107
+ result = result or await _utils.wrap_to_async_func(r)(retry_state)
108
+ if result:
109
+ break
110
+ return result
111
+
112
+
113
+ class retry_all(async_retry_base):
114
+ """Retries if all the retries condition are valid."""
115
+
116
+ def __init__(self, *retries: typing.Union[retry_base, async_retry_base]) -> None:
117
+ self.retries = retries
118
+
119
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
120
+ result = True
121
+ for r in self.retries:
122
+ result = result and await _utils.wrap_to_async_func(r)(retry_state)
123
+ if not result:
124
+ break
125
+ return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tenacity
3
- Version: 8.4.0
3
+ Version: 8.4.2
4
4
  Summary: Retry code until it succeeds
5
5
  Home-page: https://github.com/jd/tenacity
6
6
  Author: Julien Danjou
@@ -1,4 +1,4 @@
1
- tenacity/__init__.py,sha256=dT216tDrncc6QKV6_1AfuhzmCMfUzM_hIwW34ivIRsQ,23651
1
+ tenacity/__init__.py,sha256=_KlyQOzUwzhJeW9qH-_hoyFzV-AZ_aRg2f5qei8o_0I,24026
2
2
  tenacity/_utils.py,sha256=5AwPoFrGOIfPkCtqeJdFBi7KKQNcJXJ3erbtGOXtn6w,2916
3
3
  tenacity/after.py,sha256=NR4rGyslG7xF1hDJZb2Wf8wVApafX0HZwz2nFVLvaqE,1658
4
4
  tenacity/before.py,sha256=7zDTpZ3b6rkY9sOdS-qbpjBgSjVr3xBqcIqdYAh9ZKM,1544
@@ -9,8 +9,10 @@ tenacity/retry.py,sha256=PVnZfXBeLN2DzsytKexw23SPLn82AdZfFPC2OhVQ_1Y,8986
9
9
  tenacity/stop.py,sha256=wQuwGfCLw8OH1C3x0G9lH9xtJCyhgviePQ40HRAUg54,4113
10
10
  tenacity/tornadoweb.py,sha256=vS1ONfPYoGzPx1asQaVbfoo6D9tPIzSysJipm61Yqw8,2125
11
11
  tenacity/wait.py,sha256=Q9XoZCtFra53aQOyfABpvRDuUeB-NpUUXImHsUiRQI0,8042
12
- tenacity-8.4.0.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
13
- tenacity-8.4.0.dist-info/METADATA,sha256=rnsKmqyLFhyN5Ydqjehhxuls6kPQ27yu6M4l2t5WeuM,1155
14
- tenacity-8.4.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
15
- tenacity-8.4.0.dist-info/top_level.txt,sha256=Zf8AOZMN7hr1EEcUo9U5KzXsM4TOC1pBZ22D8913JYs,9
16
- tenacity-8.4.0.dist-info/RECORD,,
12
+ tenacity/asyncio/__init__.py,sha256=SWnrZkyPTvQHvhTAcPVTLCT3LVxg5ICzhUcyt5tMxsE,7782
13
+ tenacity/asyncio/retry.py,sha256=ymu8F1JfAerc5vDO0V4-2kCvHXqyce2jpO1Nlt6aKxI,4244
14
+ tenacity-8.4.2.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
15
+ tenacity-8.4.2.dist-info/METADATA,sha256=lEJwjb1dtrlcvg7A-aGxccyqNws7q-sROWoF_1U_O44,1155
16
+ tenacity-8.4.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
17
+ tenacity-8.4.2.dist-info/top_level.txt,sha256=Zf8AOZMN7hr1EEcUo9U5KzXsM4TOC1pBZ22D8913JYs,9
18
+ tenacity-8.4.2.dist-info/RECORD,,