Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pip/_vendor/tenacity/__init__.py: 51%
275 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:48 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:48 +0000
1# Copyright 2016-2018 Julien Danjou
2# Copyright 2017 Elisey Zanko
3# Copyright 2016 Étienne Bersac
4# Copyright 2016 Joshua Harlow
5# Copyright 2013-2014 Ray Holder
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
20import functools
21import sys
22import threading
23import time
24import typing as t
25import warnings
26from abc import ABC, abstractmethod
27from concurrent import futures
28from inspect import iscoroutinefunction
30# Import all built-in retry strategies for easier usage.
31from .retry import retry_base # noqa
32from .retry import retry_all # noqa
33from .retry import retry_always # noqa
34from .retry import retry_any # noqa
35from .retry import retry_if_exception # noqa
36from .retry import retry_if_exception_type # noqa
37from .retry import retry_if_exception_cause_type # noqa
38from .retry import retry_if_not_exception_type # noqa
39from .retry import retry_if_not_result # noqa
40from .retry import retry_if_result # noqa
41from .retry import retry_never # noqa
42from .retry import retry_unless_exception_type # noqa
43from .retry import retry_if_exception_message # noqa
44from .retry import retry_if_not_exception_message # noqa
46# Import all nap strategies for easier usage.
47from .nap import sleep # noqa
48from .nap import sleep_using_event # noqa
50# Import all built-in stop strategies for easier usage.
51from .stop import stop_after_attempt # noqa
52from .stop import stop_after_delay # noqa
53from .stop import stop_all # noqa
54from .stop import stop_any # noqa
55from .stop import stop_never # noqa
56from .stop import stop_when_event_set # noqa
58# Import all built-in wait strategies for easier usage.
59from .wait import wait_chain # noqa
60from .wait import wait_combine # noqa
61from .wait import wait_exponential # noqa
62from .wait import wait_fixed # noqa
63from .wait import wait_incrementing # noqa
64from .wait import wait_none # noqa
65from .wait import wait_random # noqa
66from .wait import wait_random_exponential # noqa
67from .wait import wait_random_exponential as wait_full_jitter # noqa
68from .wait import wait_exponential_jitter # noqa
70# Import all built-in before strategies for easier usage.
71from .before import before_log # noqa
72from .before import before_nothing # noqa
74# Import all built-in after strategies for easier usage.
75from .after import after_log # noqa
76from .after import after_nothing # noqa
78# Import all built-in after strategies for easier usage.
79from .before_sleep import before_sleep_log # noqa
80from .before_sleep import before_sleep_nothing # noqa
82# Replace a conditional import with a hard-coded None so that pip does
83# not attempt to use tornado even if it is present in the environment.
84# If tornado is non-None, tenacity will attempt to execute some code
85# that is sensitive to the version of tornado, which could break pip
86# if an old version is found.
87tornado = None # type: ignore
89if t.TYPE_CHECKING:
90 import types
92 from .retry import RetryBaseT
93 from .stop import StopBaseT
94 from .wait import WaitBaseT
97WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
98WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any])
101class TryAgain(Exception):
102 """Always retry the executed function when raised."""
105NO_RESULT = object()
108class DoAttempt:
109 pass
112class DoSleep(float):
113 pass
116class BaseAction:
117 """Base class for representing actions to take by retry object.
119 Concrete implementations must define:
120 - __init__: to initialize all necessary fields
121 - REPR_FIELDS: class variable specifying attributes to include in repr(self)
122 - NAME: for identification in retry object methods and callbacks
123 """
125 REPR_FIELDS: t.Sequence[str] = ()
126 NAME: t.Optional[str] = None
128 def __repr__(self) -> str:
129 state_str = ", ".join(f"{field}={getattr(self, field)!r}" for field in self.REPR_FIELDS)
130 return f"{self.__class__.__name__}({state_str})"
132 def __str__(self) -> str:
133 return repr(self)
136class RetryAction(BaseAction):
137 REPR_FIELDS = ("sleep",)
138 NAME = "retry"
140 def __init__(self, sleep: t.SupportsFloat) -> None:
141 self.sleep = float(sleep)
144_unset = object()
147def _first_set(first: t.Union[t.Any, object], second: t.Any) -> t.Any:
148 return second if first is _unset else first
151class RetryError(Exception):
152 """Encapsulates the last attempt instance right before giving up."""
154 def __init__(self, last_attempt: "Future") -> None:
155 self.last_attempt = last_attempt
156 super().__init__(last_attempt)
158 def reraise(self) -> "t.NoReturn":
159 if self.last_attempt.failed:
160 raise self.last_attempt.result()
161 raise self
163 def __str__(self) -> str:
164 return f"{self.__class__.__name__}[{self.last_attempt}]"
167class AttemptManager:
168 """Manage attempt context."""
170 def __init__(self, retry_state: "RetryCallState"):
171 self.retry_state = retry_state
173 def __enter__(self) -> None:
174 pass
176 def __exit__(
177 self,
178 exc_type: t.Optional[t.Type[BaseException]],
179 exc_value: t.Optional[BaseException],
180 traceback: t.Optional["types.TracebackType"],
181 ) -> t.Optional[bool]:
182 if exc_type is not None and exc_value is not None:
183 self.retry_state.set_exception((exc_type, exc_value, traceback))
184 return True # Swallow exception.
185 else:
186 # We don't have the result, actually.
187 self.retry_state.set_result(None)
188 return None
191class BaseRetrying(ABC):
192 def __init__(
193 self,
194 sleep: t.Callable[[t.Union[int, float]], None] = sleep,
195 stop: "StopBaseT" = stop_never,
196 wait: "WaitBaseT" = wait_none(),
197 retry: "RetryBaseT" = retry_if_exception_type(),
198 before: t.Callable[["RetryCallState"], None] = before_nothing,
199 after: t.Callable[["RetryCallState"], None] = after_nothing,
200 before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
201 reraise: bool = False,
202 retry_error_cls: t.Type[RetryError] = RetryError,
203 retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None,
204 ):
205 self.sleep = sleep
206 self.stop = stop
207 self.wait = wait
208 self.retry = retry
209 self.before = before
210 self.after = after
211 self.before_sleep = before_sleep
212 self.reraise = reraise
213 self._local = threading.local()
214 self.retry_error_cls = retry_error_cls
215 self.retry_error_callback = retry_error_callback
217 def copy(
218 self,
219 sleep: t.Union[t.Callable[[t.Union[int, float]], None], object] = _unset,
220 stop: t.Union["StopBaseT", object] = _unset,
221 wait: t.Union["WaitBaseT", object] = _unset,
222 retry: t.Union[retry_base, object] = _unset,
223 before: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
224 after: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
225 before_sleep: t.Union[t.Optional[t.Callable[["RetryCallState"], None]], object] = _unset,
226 reraise: t.Union[bool, object] = _unset,
227 retry_error_cls: t.Union[t.Type[RetryError], object] = _unset,
228 retry_error_callback: t.Union[t.Optional[t.Callable[["RetryCallState"], t.Any]], object] = _unset,
229 ) -> "BaseRetrying":
230 """Copy this object with some parameters changed if needed."""
231 return self.__class__(
232 sleep=_first_set(sleep, self.sleep),
233 stop=_first_set(stop, self.stop),
234 wait=_first_set(wait, self.wait),
235 retry=_first_set(retry, self.retry),
236 before=_first_set(before, self.before),
237 after=_first_set(after, self.after),
238 before_sleep=_first_set(before_sleep, self.before_sleep),
239 reraise=_first_set(reraise, self.reraise),
240 retry_error_cls=_first_set(retry_error_cls, self.retry_error_cls),
241 retry_error_callback=_first_set(retry_error_callback, self.retry_error_callback),
242 )
244 def __repr__(self) -> str:
245 return (
246 f"<{self.__class__.__name__} object at 0x{id(self):x} ("
247 f"stop={self.stop}, "
248 f"wait={self.wait}, "
249 f"sleep={self.sleep}, "
250 f"retry={self.retry}, "
251 f"before={self.before}, "
252 f"after={self.after})>"
253 )
255 @property
256 def statistics(self) -> t.Dict[str, t.Any]:
257 """Return a dictionary of runtime statistics.
259 This dictionary will be empty when the controller has never been
260 ran. When it is running or has ran previously it should have (but
261 may not) have useful and/or informational keys and values when
262 running is underway and/or completed.
264 .. warning:: The keys in this dictionary **should** be some what
265 stable (not changing), but there existence **may**
266 change between major releases as new statistics are
267 gathered or removed so before accessing keys ensure that
268 they actually exist and handle when they do not.
270 .. note:: The values in this dictionary are local to the thread
271 running call (so if multiple threads share the same retrying
272 object - either directly or indirectly) they will each have
273 there own view of statistics they have collected (in the
274 future we may provide a way to aggregate the various
275 statistics from each thread).
276 """
277 try:
278 return self._local.statistics # type: ignore[no-any-return]
279 except AttributeError:
280 self._local.statistics = t.cast(t.Dict[str, t.Any], {})
281 return self._local.statistics
283 def wraps(self, f: WrappedFn) -> WrappedFn:
284 """Wrap a function for retrying.
286 :param f: A function to wraps for retrying.
287 """
289 @functools.wraps(f)
290 def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any:
291 return self(f, *args, **kw)
293 def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:
294 return self.copy(*args, **kwargs).wraps(f)
296 wrapped_f.retry = self # type: ignore[attr-defined]
297 wrapped_f.retry_with = retry_with # type: ignore[attr-defined]
299 return wrapped_f # type: ignore[return-value]
301 def begin(self) -> None:
302 self.statistics.clear()
303 self.statistics["start_time"] = time.monotonic()
304 self.statistics["attempt_number"] = 1
305 self.statistics["idle_for"] = 0
307 def iter(self, retry_state: "RetryCallState") -> t.Union[DoAttempt, DoSleep, t.Any]: # noqa
308 fut = retry_state.outcome
309 if fut is None:
310 if self.before is not None:
311 self.before(retry_state)
312 return DoAttempt()
314 is_explicit_retry = fut.failed and isinstance(fut.exception(), TryAgain)
315 if not (is_explicit_retry or self.retry(retry_state)):
316 return fut.result()
318 if self.after is not None:
319 self.after(retry_state)
321 self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start
322 if self.stop(retry_state):
323 if self.retry_error_callback:
324 return self.retry_error_callback(retry_state)
325 retry_exc = self.retry_error_cls(fut)
326 if self.reraise:
327 raise retry_exc.reraise()
328 raise retry_exc from fut.exception()
330 if self.wait:
331 sleep = self.wait(retry_state)
332 else:
333 sleep = 0.0
334 retry_state.next_action = RetryAction(sleep)
335 retry_state.idle_for += sleep
336 self.statistics["idle_for"] += sleep
337 self.statistics["attempt_number"] += 1
339 if self.before_sleep is not None:
340 self.before_sleep(retry_state)
342 return DoSleep(sleep)
344 def __iter__(self) -> t.Generator[AttemptManager, None, None]:
345 self.begin()
347 retry_state = RetryCallState(self, fn=None, args=(), kwargs={})
348 while True:
349 do = self.iter(retry_state=retry_state)
350 if isinstance(do, DoAttempt):
351 yield AttemptManager(retry_state=retry_state)
352 elif isinstance(do, DoSleep):
353 retry_state.prepare_for_next_attempt()
354 self.sleep(do)
355 else:
356 break
358 @abstractmethod
359 def __call__(
360 self,
361 fn: t.Callable[..., WrappedFnReturnT],
362 *args: t.Any,
363 **kwargs: t.Any,
364 ) -> WrappedFnReturnT:
365 pass
368class Retrying(BaseRetrying):
369 """Retrying controller."""
371 def __call__(
372 self,
373 fn: t.Callable[..., WrappedFnReturnT],
374 *args: t.Any,
375 **kwargs: t.Any,
376 ) -> WrappedFnReturnT:
377 self.begin()
379 retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
380 while True:
381 do = self.iter(retry_state=retry_state)
382 if isinstance(do, DoAttempt):
383 try:
384 result = fn(*args, **kwargs)
385 except BaseException: # noqa: B902
386 retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
387 else:
388 retry_state.set_result(result)
389 elif isinstance(do, DoSleep):
390 retry_state.prepare_for_next_attempt()
391 self.sleep(do)
392 else:
393 return do # type: ignore[no-any-return]
396if sys.version_info[1] >= 9:
397 FutureGenericT = futures.Future[t.Any]
398else:
399 FutureGenericT = futures.Future
402class Future(FutureGenericT):
403 """Encapsulates a (future or past) attempted call to a target function."""
405 def __init__(self, attempt_number: int) -> None:
406 super().__init__()
407 self.attempt_number = attempt_number
409 @property
410 def failed(self) -> bool:
411 """Return whether a exception is being held in this future."""
412 return self.exception() is not None
414 @classmethod
415 def construct(cls, attempt_number: int, value: t.Any, has_exception: bool) -> "Future":
416 """Construct a new Future object."""
417 fut = cls(attempt_number)
418 if has_exception:
419 fut.set_exception(value)
420 else:
421 fut.set_result(value)
422 return fut
425class RetryCallState:
426 """State related to a single call wrapped with Retrying."""
428 def __init__(
429 self,
430 retry_object: BaseRetrying,
431 fn: t.Optional[WrappedFn],
432 args: t.Any,
433 kwargs: t.Any,
434 ) -> None:
435 #: Retry call start timestamp
436 self.start_time = time.monotonic()
437 #: Retry manager object
438 self.retry_object = retry_object
439 #: Function wrapped by this retry call
440 self.fn = fn
441 #: Arguments of the function wrapped by this retry call
442 self.args = args
443 #: Keyword arguments of the function wrapped by this retry call
444 self.kwargs = kwargs
446 #: The number of the current attempt
447 self.attempt_number: int = 1
448 #: Last outcome (result or exception) produced by the function
449 self.outcome: t.Optional[Future] = None
450 #: Timestamp of the last outcome
451 self.outcome_timestamp: t.Optional[float] = None
452 #: Time spent sleeping in retries
453 self.idle_for: float = 0.0
454 #: Next action as decided by the retry manager
455 self.next_action: t.Optional[RetryAction] = None
457 @property
458 def seconds_since_start(self) -> t.Optional[float]:
459 if self.outcome_timestamp is None:
460 return None
461 return self.outcome_timestamp - self.start_time
463 def prepare_for_next_attempt(self) -> None:
464 self.outcome = None
465 self.outcome_timestamp = None
466 self.attempt_number += 1
467 self.next_action = None
469 def set_result(self, val: t.Any) -> None:
470 ts = time.monotonic()
471 fut = Future(self.attempt_number)
472 fut.set_result(val)
473 self.outcome, self.outcome_timestamp = fut, ts
475 def set_exception(
476 self, exc_info: t.Tuple[t.Type[BaseException], BaseException, "types.TracebackType| None"]
477 ) -> None:
478 ts = time.monotonic()
479 fut = Future(self.attempt_number)
480 fut.set_exception(exc_info[1])
481 self.outcome, self.outcome_timestamp = fut, ts
483 def __repr__(self) -> str:
484 if self.outcome is None:
485 result = "none yet"
486 elif self.outcome.failed:
487 exception = self.outcome.exception()
488 result = f"failed ({exception.__class__.__name__} {exception})"
489 else:
490 result = f"returned {self.outcome.result()}"
492 slept = float(round(self.idle_for, 2))
493 clsname = self.__class__.__name__
494 return f"<{clsname} {id(self)}: attempt #{self.attempt_number}; slept for {slept}; last result: {result}>"
497@t.overload
498def retry(func: WrappedFn) -> WrappedFn:
499 ...
502@t.overload
503def retry(
504 sleep: t.Callable[[t.Union[int, float]], None] = sleep,
505 stop: "StopBaseT" = stop_never,
506 wait: "WaitBaseT" = wait_none(),
507 retry: "RetryBaseT" = retry_if_exception_type(),
508 before: t.Callable[["RetryCallState"], None] = before_nothing,
509 after: t.Callable[["RetryCallState"], None] = after_nothing,
510 before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
511 reraise: bool = False,
512 retry_error_cls: t.Type["RetryError"] = RetryError,
513 retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None,
514) -> t.Callable[[WrappedFn], WrappedFn]:
515 ...
518def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any:
519 """Wrap a function with a new `Retrying` object.
521 :param dargs: positional arguments passed to Retrying object
522 :param dkw: keyword arguments passed to the Retrying object
523 """
524 # support both @retry and @retry() as valid syntax
525 if len(dargs) == 1 and callable(dargs[0]):
526 return retry()(dargs[0])
527 else:
529 def wrap(f: WrappedFn) -> WrappedFn:
530 if isinstance(f, retry_base):
531 warnings.warn(
532 f"Got retry_base instance ({f.__class__.__name__}) as callable argument, "
533 f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"
534 )
535 r: "BaseRetrying"
536 if iscoroutinefunction(f):
537 r = AsyncRetrying(*dargs, **dkw)
538 elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f):
539 r = TornadoRetrying(*dargs, **dkw)
540 else:
541 r = Retrying(*dargs, **dkw)
543 return r.wraps(f)
545 return wrap
548from pip._vendor.tenacity._asyncio import AsyncRetrying # noqa:E402,I100
550if tornado:
551 from pip._vendor.tenacity.tornadoweb import TornadoRetrying
554__all__ = [
555 "retry_base",
556 "retry_all",
557 "retry_always",
558 "retry_any",
559 "retry_if_exception",
560 "retry_if_exception_type",
561 "retry_if_exception_cause_type",
562 "retry_if_not_exception_type",
563 "retry_if_not_result",
564 "retry_if_result",
565 "retry_never",
566 "retry_unless_exception_type",
567 "retry_if_exception_message",
568 "retry_if_not_exception_message",
569 "sleep",
570 "sleep_using_event",
571 "stop_after_attempt",
572 "stop_after_delay",
573 "stop_all",
574 "stop_any",
575 "stop_never",
576 "stop_when_event_set",
577 "wait_chain",
578 "wait_combine",
579 "wait_exponential",
580 "wait_fixed",
581 "wait_incrementing",
582 "wait_none",
583 "wait_random",
584 "wait_random_exponential",
585 "wait_full_jitter",
586 "wait_exponential_jitter",
587 "before_log",
588 "before_nothing",
589 "after_log",
590 "after_nothing",
591 "before_sleep_log",
592 "before_sleep_nothing",
593 "retry",
594 "WrappedFn",
595 "TryAgain",
596 "NO_RESULT",
597 "DoAttempt",
598 "DoSleep",
599 "BaseAction",
600 "RetryAction",
601 "RetryError",
602 "AttemptManager",
603 "BaseRetrying",
604 "Retrying",
605 "Future",
606 "RetryCallState",
607 "AsyncRetrying",
608]