Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tenacity/retry.py: 39%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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.
17import abc
18import re
19import typing
21if typing.TYPE_CHECKING:
22 from tenacity import RetryCallState
25class retry_base(abc.ABC):
26 """Abstract base class for retry strategies."""
28 @abc.abstractmethod
29 def __call__(self, retry_state: "RetryCallState") -> bool:
30 pass
32 def __and__(self, other: "retry_base") -> "retry_all":
33 return other.__rand__(self)
35 def __rand__(self, other: "retry_base") -> "retry_all":
36 return retry_all(other, self)
38 def __or__(self, other: "retry_base") -> "retry_any":
39 return other.__ror__(self)
41 def __ror__(self, other: "retry_base") -> "retry_any":
42 return retry_any(other, self)
45RetryBaseT = typing.Union[retry_base, typing.Callable[["RetryCallState"], bool]]
48class _retry_never(retry_base):
49 """Retry strategy that never rejects any result."""
51 def __call__(self, retry_state: "RetryCallState") -> bool:
52 return False
55retry_never = _retry_never()
58class _retry_always(retry_base):
59 """Retry strategy that always rejects any result."""
61 def __call__(self, retry_state: "RetryCallState") -> bool:
62 return True
65retry_always = _retry_always()
68class retry_if_exception(retry_base):
69 """Retry strategy that retries if an exception verifies a predicate."""
71 def __init__(self, predicate: typing.Callable[[BaseException], bool]) -> None:
72 self.predicate = predicate
74 def __call__(self, retry_state: "RetryCallState") -> bool:
75 if retry_state.outcome is None:
76 raise RuntimeError("__call__() called before outcome was set")
78 if retry_state.outcome.failed:
79 exception = retry_state.outcome.exception()
80 if exception is None:
81 raise RuntimeError("outcome failed but the exception is None")
82 return self.predicate(exception)
83 else:
84 return False
87class retry_if_exception_type(retry_if_exception):
88 """Retries if an exception has been raised of one or more types."""
90 def __init__(
91 self,
92 exception_types: typing.Union[
93 typing.Type[BaseException],
94 typing.Tuple[typing.Type[BaseException], ...],
95 ] = Exception,
96 ) -> None:
97 self.exception_types = exception_types
98 super().__init__(lambda e: isinstance(e, exception_types))
101class retry_if_not_exception_type(retry_if_exception):
102 """Retries except an exception has been raised of one or more types."""
104 def __init__(
105 self,
106 exception_types: typing.Union[
107 typing.Type[BaseException],
108 typing.Tuple[typing.Type[BaseException], ...],
109 ] = Exception,
110 ) -> None:
111 self.exception_types = exception_types
112 super().__init__(lambda e: not isinstance(e, exception_types))
115class retry_unless_exception_type(retry_if_exception):
116 """Retries until an exception is raised of one or more types."""
118 def __init__(
119 self,
120 exception_types: typing.Union[
121 typing.Type[BaseException],
122 typing.Tuple[typing.Type[BaseException], ...],
123 ] = Exception,
124 ) -> None:
125 self.exception_types = exception_types
126 super().__init__(lambda e: not isinstance(e, exception_types))
128 def __call__(self, retry_state: "RetryCallState") -> bool:
129 if retry_state.outcome is None:
130 raise RuntimeError("__call__() called before outcome was set")
132 # always retry if no exception was raised
133 if not retry_state.outcome.failed:
134 return True
136 exception = retry_state.outcome.exception()
137 if exception is None:
138 raise RuntimeError("outcome failed but the exception is None")
139 return self.predicate(exception)
142class retry_if_exception_cause_type(retry_base):
143 """Retries if any of the causes of the raised exception is of one or more types.
145 The check on the type of the cause of the exception is done recursively (until finding
146 an exception in the chain that has no `__cause__`)
147 """
149 def __init__(
150 self,
151 exception_types: typing.Union[
152 typing.Type[BaseException],
153 typing.Tuple[typing.Type[BaseException], ...],
154 ] = Exception,
155 ) -> None:
156 self.exception_cause_types = exception_types
158 def __call__(self, retry_state: "RetryCallState") -> bool:
159 if retry_state.outcome is None:
160 raise RuntimeError("__call__ called before outcome was set")
162 if retry_state.outcome.failed:
163 exc = retry_state.outcome.exception()
164 while exc is not None:
165 if isinstance(exc.__cause__, self.exception_cause_types):
166 return True
167 exc = exc.__cause__
169 return False
172class retry_if_result(retry_base):
173 """Retries if the result verifies a predicate."""
175 def __init__(self, predicate: typing.Callable[[typing.Any], bool]) -> None:
176 self.predicate = predicate
178 def __call__(self, retry_state: "RetryCallState") -> bool:
179 if retry_state.outcome is None:
180 raise RuntimeError("__call__() called before outcome was set")
182 if not retry_state.outcome.failed:
183 return self.predicate(retry_state.outcome.result())
184 else:
185 return False
188class retry_if_not_result(retry_base):
189 """Retries if the result refutes a predicate."""
191 def __init__(self, predicate: typing.Callable[[typing.Any], bool]) -> None:
192 self.predicate = predicate
194 def __call__(self, retry_state: "RetryCallState") -> bool:
195 if retry_state.outcome is None:
196 raise RuntimeError("__call__() called before outcome was set")
198 if not retry_state.outcome.failed:
199 return not self.predicate(retry_state.outcome.result())
200 else:
201 return False
204class retry_if_exception_message(retry_if_exception):
205 """Retries if an exception message equals or matches."""
207 def __init__(
208 self,
209 message: typing.Optional[str] = None,
210 match: typing.Union[None, str, typing.Pattern[str]] = None,
211 ) -> None:
212 if message and match:
213 raise TypeError(
214 f"{self.__class__.__name__}() takes either 'message' or 'match', not both"
215 )
217 # set predicate
218 if message:
220 def message_fnc(exception: BaseException) -> bool:
221 return message == str(exception)
223 predicate = message_fnc
224 elif match:
225 prog = re.compile(match)
227 def match_fnc(exception: BaseException) -> bool:
228 return bool(prog.match(str(exception)))
230 predicate = match_fnc
231 else:
232 raise TypeError(
233 f"{self.__class__.__name__}() missing 1 required argument 'message' or 'match'"
234 )
236 super().__init__(predicate)
239class retry_if_not_exception_message(retry_if_exception_message):
240 """Retries until an exception message equals or matches."""
242 def __init__(
243 self,
244 message: typing.Optional[str] = None,
245 match: typing.Union[None, str, typing.Pattern[str]] = None,
246 ) -> None:
247 super().__init__(message, match)
248 # invert predicate
249 if_predicate = self.predicate
250 self.predicate = lambda *args_, **kwargs_: not if_predicate(*args_, **kwargs_)
252 def __call__(self, retry_state: "RetryCallState") -> bool:
253 if retry_state.outcome is None:
254 raise RuntimeError("__call__() called before outcome was set")
256 if not retry_state.outcome.failed:
257 return True
259 exception = retry_state.outcome.exception()
260 if exception is None:
261 raise RuntimeError("outcome failed but the exception is None")
262 return self.predicate(exception)
265class retry_any(retry_base):
266 """Retries if any of the retries condition is valid."""
268 def __init__(self, *retries: retry_base) -> None:
269 self.retries = retries
271 def __call__(self, retry_state: "RetryCallState") -> bool:
272 return any(r(retry_state) for r in self.retries)
275class retry_all(retry_base):
276 """Retries if all the retries condition are valid."""
278 def __init__(self, *retries: retry_base) -> None:
279 self.retries = retries
281 def __call__(self, retry_state: "RetryCallState") -> bool:
282 return all(r(retry_state) for r in self.retries)