Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tenacity/__init__.py: 51%
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-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.
18import dataclasses
19import functools
20import sys
21import threading
22import time
23import types
24import typing as t
25import warnings
26from abc import ABC, abstractmethod
27from concurrent import futures
29from . import _utils
31# Import all built-in retry strategies for easier usage.
32from .retry import retry_base # noqa
33from .retry import retry_all # noqa
34from .retry import retry_always # noqa
35from .retry import retry_any # noqa
36from .retry import retry_if_exception # noqa
37from .retry import retry_if_exception_type # noqa
38from .retry import retry_if_exception_cause_type # noqa
39from .retry import retry_if_not_exception_type # noqa
40from .retry import retry_if_not_result # noqa
41from .retry import retry_if_result # noqa
42from .retry import retry_never # noqa
43from .retry import retry_unless_exception_type # noqa
44from .retry import retry_if_exception_message # noqa
45from .retry import retry_if_not_exception_message # noqa
47# Import all nap strategies for easier usage.
48from .nap import sleep # noqa
49from .nap import sleep_using_event # noqa
51# Import all built-in stop strategies for easier usage.
52from .stop import stop_after_attempt # noqa
53from .stop import stop_after_delay # noqa
54from .stop import stop_before_delay # noqa
55from .stop import stop_all # noqa
56from .stop import stop_any # noqa
57from .stop import stop_never # noqa
58from .stop import stop_when_event_set # noqa
60# Import all built-in wait strategies for easier usage.
61from .wait import wait_chain # noqa
62from .wait import wait_combine # noqa
63from .wait import wait_exception # noqa
64from .wait import wait_exponential # noqa
65from .wait import wait_fixed # noqa
66from .wait import wait_incrementing # noqa
67from .wait import wait_none # noqa
68from .wait import wait_random # noqa
69from .wait import wait_random_exponential # noqa
70from .wait import wait_random_exponential as wait_full_jitter # noqa
71from .wait import wait_exponential_jitter # noqa
73# Import all built-in before strategies for easier usage.
74from .before import before_log # noqa
75from .before import before_nothing # noqa
77# Import all built-in after strategies for easier usage.
78from .after import after_log # noqa
79from .after import after_nothing # noqa
81# Import all built-in before sleep strategies for easier usage.
82from .before_sleep import before_sleep_log # noqa
83from .before_sleep import before_sleep_nothing # noqa
85try:
86 import tornado
87except ImportError:
88 tornado = None
90if t.TYPE_CHECKING:
91 from typing_extensions import Self
93 from . import asyncio as tasyncio
94 from .retry import RetryBaseT
95 from .stop import StopBaseT
96 from .wait import WaitBaseT
99WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
100WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any])
101P = t.ParamSpec("P")
102R = t.TypeVar("R")
105@dataclasses.dataclass(slots=True)
106class IterState:
107 actions: t.List[t.Callable[["RetryCallState"], t.Any]] = dataclasses.field(
108 default_factory=list
109 )
110 retry_run_result: bool = False
111 delay_since_first_attempt: int = 0
112 stop_run_result: bool = False
113 is_explicit_retry: bool = False
115 def reset(self) -> None:
116 self.actions = []
117 self.retry_run_result = False
118 self.delay_since_first_attempt = 0
119 self.stop_run_result = False
120 self.is_explicit_retry = False
123class TryAgain(Exception):
124 """Always retry the executed function when raised."""
127NO_RESULT = object()
130class DoAttempt:
131 pass
134class DoSleep(float):
135 pass
138class BaseAction:
139 """Base class for representing actions to take by retry object.
141 Concrete implementations must define:
142 - __init__: to initialize all necessary fields
143 - REPR_FIELDS: class variable specifying attributes to include in repr(self)
144 - NAME: for identification in retry object methods and callbacks
145 """
147 REPR_FIELDS: t.Sequence[str] = ()
148 NAME: t.Optional[str] = None
150 def __repr__(self) -> str:
151 state_str = ", ".join(
152 f"{field}={getattr(self, field)!r}" for field in self.REPR_FIELDS
153 )
154 return f"{self.__class__.__name__}({state_str})"
156 def __str__(self) -> str:
157 return repr(self)
160class RetryAction(BaseAction):
161 REPR_FIELDS = ("sleep",)
162 NAME = "retry"
164 def __init__(self, sleep: t.SupportsFloat) -> None:
165 self.sleep = float(sleep)
168_unset = object()
171def _first_set(first: t.Union[t.Any, object], second: t.Any) -> t.Any:
172 return second if first is _unset else first
175class RetryError(Exception):
176 """Encapsulates the last attempt instance right before giving up."""
178 def __init__(self, last_attempt: "Future") -> None:
179 self.last_attempt = last_attempt
180 super().__init__(last_attempt)
182 def reraise(self) -> t.NoReturn:
183 if self.last_attempt.failed:
184 raise self.last_attempt.result()
185 raise self
187 def __str__(self) -> str:
188 return f"{self.__class__.__name__}[{self.last_attempt}]"
191class AttemptManager:
192 """Manage attempt context."""
194 def __init__(self, retry_state: "RetryCallState"):
195 self.retry_state = retry_state
197 def __enter__(self) -> None:
198 pass
200 def __exit__(
201 self,
202 exc_type: t.Optional[t.Type[BaseException]],
203 exc_value: t.Optional[BaseException],
204 traceback: t.Optional["types.TracebackType"],
205 ) -> t.Optional[bool]:
206 if exc_type is not None and exc_value is not None:
207 self.retry_state.set_exception((exc_type, exc_value, traceback))
208 return True # Swallow exception.
209 else:
210 # We don't have the result, actually.
211 self.retry_state.set_result(None)
212 return None
215class BaseRetrying(ABC):
216 def __init__(
217 self,
218 sleep: t.Callable[[t.Union[int, float]], None] = sleep,
219 stop: "StopBaseT" = stop_never,
220 wait: "WaitBaseT" = wait_none(),
221 retry: "RetryBaseT" = retry_if_exception_type(),
222 before: t.Callable[["RetryCallState"], None] = before_nothing,
223 after: t.Callable[["RetryCallState"], None] = after_nothing,
224 before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
225 reraise: bool = False,
226 retry_error_cls: t.Type[RetryError] = RetryError,
227 retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None,
228 ):
229 self.sleep = sleep
230 self.stop = stop
231 self.wait = wait
232 self.retry = retry
233 self.before = before
234 self.after = after
235 self.before_sleep = before_sleep
236 self.reraise = reraise
237 self._local = threading.local()
238 self.retry_error_cls = retry_error_cls
239 self.retry_error_callback = retry_error_callback
241 def copy(
242 self,
243 sleep: t.Union[t.Callable[[t.Union[int, float]], None], object] = _unset,
244 stop: t.Union["StopBaseT", object] = _unset,
245 wait: t.Union["WaitBaseT", object] = _unset,
246 retry: t.Union[retry_base, object] = _unset,
247 before: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
248 after: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
249 before_sleep: t.Union[
250 t.Optional[t.Callable[["RetryCallState"], None]], object
251 ] = _unset,
252 reraise: t.Union[bool, object] = _unset,
253 retry_error_cls: t.Union[t.Type[RetryError], object] = _unset,
254 retry_error_callback: t.Union[
255 t.Optional[t.Callable[["RetryCallState"], t.Any]], object
256 ] = _unset,
257 ) -> "Self":
258 """Copy this object with some parameters changed if needed."""
259 return self.__class__(
260 sleep=_first_set(sleep, self.sleep),
261 stop=_first_set(stop, self.stop),
262 wait=_first_set(wait, self.wait),
263 retry=_first_set(retry, self.retry),
264 before=_first_set(before, self.before),
265 after=_first_set(after, self.after),
266 before_sleep=_first_set(before_sleep, self.before_sleep),
267 reraise=_first_set(reraise, self.reraise),
268 retry_error_cls=_first_set(retry_error_cls, self.retry_error_cls),
269 retry_error_callback=_first_set(
270 retry_error_callback, self.retry_error_callback
271 ),
272 )
274 def __repr__(self) -> str:
275 return (
276 f"<{self.__class__.__name__} object at 0x{id(self):x} ("
277 f"stop={self.stop}, "
278 f"wait={self.wait}, "
279 f"sleep={self.sleep}, "
280 f"retry={self.retry}, "
281 f"before={self.before}, "
282 f"after={self.after})>"
283 )
285 @property
286 def statistics(self) -> t.Dict[str, t.Any]:
287 """Return a dictionary of runtime statistics.
289 This dictionary will be empty when the controller has never been
290 ran. When it is running or has ran previously it should have (but
291 may not) have useful and/or informational keys and values when
292 running is underway and/or completed.
294 .. warning:: The keys in this dictionary **should** be some what
295 stable (not changing), but there existence **may**
296 change between major releases as new statistics are
297 gathered or removed so before accessing keys ensure that
298 they actually exist and handle when they do not.
300 .. note:: The values in this dictionary are local to the thread
301 running call (so if multiple threads share the same retrying
302 object - either directly or indirectly) they will each have
303 there own view of statistics they have collected (in the
304 future we may provide a way to aggregate the various
305 statistics from each thread).
306 """
307 if not hasattr(self._local, "statistics"):
308 self._local.statistics = t.cast(t.Dict[str, t.Any], {})
309 return self._local.statistics # type: ignore[no-any-return]
311 @property
312 def iter_state(self) -> IterState:
313 if not hasattr(self._local, "iter_state"):
314 self._local.iter_state = IterState()
315 return self._local.iter_state # type: ignore[no-any-return]
317 def wraps(self, f: WrappedFn) -> WrappedFn:
318 """Wrap a function for retrying.
320 :param f: A function to wraps for retrying.
321 """
323 @functools.wraps(
324 f, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")
325 )
326 def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any:
327 # Always create a copy to prevent overwriting the local contexts when
328 # calling the same wrapped functions multiple times in the same stack
329 copy = self.copy()
330 wrapped_f.statistics = copy.statistics # type: ignore[attr-defined]
331 return copy(f, *args, **kw)
333 def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:
334 return self.copy(*args, **kwargs).wraps(f)
336 # Preserve attributes
337 wrapped_f.retry = self # type: ignore[attr-defined]
338 wrapped_f.retry_with = retry_with # type: ignore[attr-defined]
339 wrapped_f.statistics = {} # type: ignore[attr-defined]
341 return wrapped_f # type: ignore[return-value]
343 def begin(self) -> None:
344 self.statistics.clear()
345 self.statistics["start_time"] = time.monotonic()
346 self.statistics["attempt_number"] = 1
347 self.statistics["idle_for"] = 0
349 def _add_action_func(self, fn: t.Callable[..., t.Any]) -> None:
350 self.iter_state.actions.append(fn)
352 def _run_retry(self, retry_state: "RetryCallState") -> None:
353 self.iter_state.retry_run_result = self.retry(retry_state)
355 def _run_wait(self, retry_state: "RetryCallState") -> None:
356 if self.wait:
357 sleep = self.wait(retry_state)
358 else:
359 sleep = 0.0
361 retry_state.upcoming_sleep = sleep
363 def _run_stop(self, retry_state: "RetryCallState") -> None:
364 self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start
365 self.iter_state.stop_run_result = self.stop(retry_state)
367 def iter(self, retry_state: "RetryCallState") -> t.Union[DoAttempt, DoSleep, t.Any]: # noqa
368 self._begin_iter(retry_state)
369 result = None
370 for action in self.iter_state.actions:
371 result = action(retry_state)
372 return result
374 def _begin_iter(self, retry_state: "RetryCallState") -> None: # noqa
375 self.iter_state.reset()
377 fut = retry_state.outcome
378 if fut is None:
379 if self.before is not None:
380 self._add_action_func(self.before)
381 self._add_action_func(lambda rs: DoAttempt())
382 return
384 self.iter_state.is_explicit_retry = fut.failed and isinstance(
385 fut.exception(), TryAgain
386 )
387 if not self.iter_state.is_explicit_retry:
388 self._add_action_func(self._run_retry)
389 self._add_action_func(self._post_retry_check_actions)
391 def _post_retry_check_actions(self, retry_state: "RetryCallState") -> None:
392 if not (self.iter_state.is_explicit_retry or self.iter_state.retry_run_result):
393 self._add_action_func(lambda rs: rs.outcome.result())
394 return
396 if self.after is not None:
397 self._add_action_func(self.after)
399 self._add_action_func(self._run_wait)
400 self._add_action_func(self._run_stop)
401 self._add_action_func(self._post_stop_check_actions)
403 def _post_stop_check_actions(self, retry_state: "RetryCallState") -> None:
404 if self.iter_state.stop_run_result:
405 if self.retry_error_callback:
406 self._add_action_func(self.retry_error_callback)
407 return
409 def exc_check(rs: "RetryCallState") -> None:
410 fut = t.cast(Future, rs.outcome)
411 retry_exc = self.retry_error_cls(fut)
412 if self.reraise:
413 raise retry_exc.reraise()
414 raise retry_exc from fut.exception()
416 self._add_action_func(exc_check)
417 return
419 def next_action(rs: "RetryCallState") -> None:
420 sleep = rs.upcoming_sleep
421 rs.next_action = RetryAction(sleep)
422 rs.idle_for += sleep
423 self.statistics["idle_for"] += sleep
424 self.statistics["attempt_number"] += 1
426 self._add_action_func(next_action)
428 if self.before_sleep is not None:
429 self._add_action_func(self.before_sleep)
431 self._add_action_func(lambda rs: DoSleep(rs.upcoming_sleep))
433 def __iter__(self) -> t.Generator[AttemptManager, None, None]:
434 self.begin()
436 retry_state = RetryCallState(self, fn=None, args=(), kwargs={})
437 while True:
438 do = self.iter(retry_state=retry_state)
439 if isinstance(do, DoAttempt):
440 yield AttemptManager(retry_state=retry_state)
441 elif isinstance(do, DoSleep):
442 retry_state.prepare_for_next_attempt()
443 self.sleep(do)
444 else:
445 break
447 @abstractmethod
448 def __call__(
449 self,
450 fn: t.Callable[..., WrappedFnReturnT],
451 *args: t.Any,
452 **kwargs: t.Any,
453 ) -> WrappedFnReturnT:
454 pass
457class Retrying(BaseRetrying):
458 """Retrying controller."""
460 def __call__(
461 self,
462 fn: t.Callable[..., WrappedFnReturnT],
463 *args: t.Any,
464 **kwargs: t.Any,
465 ) -> WrappedFnReturnT:
466 self.begin()
468 retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
469 while True:
470 do = self.iter(retry_state=retry_state)
471 if isinstance(do, DoAttempt):
472 try:
473 result = fn(*args, **kwargs)
474 except BaseException: # noqa: B902
475 retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
476 else:
477 retry_state.set_result(result)
478 elif isinstance(do, DoSleep):
479 retry_state.prepare_for_next_attempt()
480 self.sleep(do)
481 else:
482 return do # type: ignore[no-any-return]
485class Future(futures.Future[t.Any]):
486 """Encapsulates a (future or past) attempted call to a target function."""
488 def __init__(self, attempt_number: int) -> None:
489 super().__init__()
490 self.attempt_number = attempt_number
492 @property
493 def failed(self) -> bool:
494 """Return whether a exception is being held in this future."""
495 return self.exception() is not None
497 @classmethod
498 def construct(
499 cls, attempt_number: int, value: t.Any, has_exception: bool
500 ) -> "Future":
501 """Construct a new Future object."""
502 fut = cls(attempt_number)
503 if has_exception:
504 fut.set_exception(value)
505 else:
506 fut.set_result(value)
507 return fut
510class RetryCallState:
511 """State related to a single call wrapped with Retrying."""
513 def __init__(
514 self,
515 retry_object: BaseRetrying,
516 fn: t.Optional[WrappedFn],
517 args: t.Any,
518 kwargs: t.Any,
519 ) -> None:
520 #: Retry call start timestamp
521 self.start_time = time.monotonic()
522 #: Retry manager object
523 self.retry_object = retry_object
524 #: Function wrapped by this retry call
525 self.fn = fn
526 #: Arguments of the function wrapped by this retry call
527 self.args = args
528 #: Keyword arguments of the function wrapped by this retry call
529 self.kwargs = kwargs
531 #: The number of the current attempt
532 self.attempt_number: int = 1
533 #: Last outcome (result or exception) produced by the function
534 self.outcome: t.Optional[Future] = None
535 #: Timestamp of the last outcome
536 self.outcome_timestamp: t.Optional[float] = None
537 #: Time spent sleeping in retries
538 self.idle_for: float = 0.0
539 #: Next action as decided by the retry manager
540 self.next_action: t.Optional[RetryAction] = None
541 #: Next sleep time as decided by the retry manager.
542 self.upcoming_sleep: float = 0.0
544 @property
545 def seconds_since_start(self) -> t.Optional[float]:
546 if self.outcome_timestamp is None:
547 return None
548 return self.outcome_timestamp - self.start_time
550 def prepare_for_next_attempt(self) -> None:
551 self.outcome = None
552 self.outcome_timestamp = None
553 self.attempt_number += 1
554 self.next_action = None
556 def set_result(self, val: t.Any) -> None:
557 ts = time.monotonic()
558 fut = Future(self.attempt_number)
559 fut.set_result(val)
560 self.outcome, self.outcome_timestamp = fut, ts
562 def set_exception(
563 self,
564 exc_info: t.Tuple[
565 t.Type[BaseException], BaseException, "types.TracebackType| None"
566 ],
567 ) -> None:
568 ts = time.monotonic()
569 fut = Future(self.attempt_number)
570 fut.set_exception(exc_info[1])
571 self.outcome, self.outcome_timestamp = fut, ts
573 def __repr__(self) -> str:
574 if self.outcome is None:
575 result = "none yet"
576 elif self.outcome.failed:
577 exception = self.outcome.exception()
578 result = f"failed ({exception.__class__.__name__} {exception})"
579 else:
580 result = f"returned {self.outcome.result()}"
582 slept = float(round(self.idle_for, 2))
583 clsname = self.__class__.__name__
584 return f"<{clsname} {id(self)}: attempt #{self.attempt_number}; slept for {slept}; last result: {result}>"
587class _AsyncRetryDecorator(t.Protocol):
588 @t.overload
589 def __call__(
590 self, fn: "t.Callable[P, types.CoroutineType[t.Any, t.Any, R]]"
591 ) -> "t.Callable[P, types.CoroutineType[t.Any, t.Any, R]]": ...
592 @t.overload
593 def __call__(
594 self, fn: t.Callable[P, t.Coroutine[t.Any, t.Any, R]]
595 ) -> t.Callable[P, t.Coroutine[t.Any, t.Any, R]]: ...
596 @t.overload
597 def __call__(
598 self, fn: t.Callable[P, t.Awaitable[R]]
599 ) -> t.Callable[P, t.Awaitable[R]]: ...
600 @t.overload
601 def __call__(self, fn: t.Callable[P, R]) -> t.Callable[P, t.Awaitable[R]]: ...
604@t.overload
605def retry(func: WrappedFn) -> WrappedFn: ...
608@t.overload
609def retry(
610 *,
611 sleep: t.Callable[[t.Union[int, float]], t.Awaitable[None]],
612 stop: "StopBaseT" = ...,
613 wait: "WaitBaseT" = ...,
614 retry: "t.Union[RetryBaseT, tasyncio.retry.RetryBaseT]" = ...,
615 before: t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]] = ...,
616 after: t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]] = ...,
617 before_sleep: t.Optional[
618 t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]]
619 ] = ...,
620 reraise: bool = ...,
621 retry_error_cls: t.Type["RetryError"] = ...,
622 retry_error_callback: t.Optional[
623 t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]]
624 ] = ...,
625) -> _AsyncRetryDecorator: ...
628@t.overload
629def retry(
630 sleep: t.Callable[[t.Union[int, float]], None] = sleep,
631 stop: "StopBaseT" = stop_never,
632 wait: "WaitBaseT" = wait_none(),
633 retry: "t.Union[RetryBaseT, tasyncio.retry.RetryBaseT]" = retry_if_exception_type(),
634 before: t.Callable[
635 ["RetryCallState"], t.Union[None, t.Awaitable[None]]
636 ] = before_nothing,
637 after: t.Callable[
638 ["RetryCallState"], t.Union[None, t.Awaitable[None]]
639 ] = after_nothing,
640 before_sleep: t.Optional[
641 t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]]
642 ] = None,
643 reraise: bool = False,
644 retry_error_cls: t.Type["RetryError"] = RetryError,
645 retry_error_callback: t.Optional[
646 t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]]
647 ] = None,
648) -> t.Callable[[WrappedFn], WrappedFn]: ...
651def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any:
652 """Wrap a function with a new `Retrying` object.
654 :param dargs: positional arguments passed to Retrying object
655 :param dkw: keyword arguments passed to the Retrying object
656 """
657 # support both @retry and @retry() as valid syntax
658 if len(dargs) == 1 and callable(dargs[0]):
659 return retry()(dargs[0])
660 else:
662 def wrap(f: WrappedFn) -> WrappedFn:
663 if isinstance(f, retry_base):
664 warnings.warn(
665 f"Got retry_base instance ({f.__class__.__name__}) as callable argument, "
666 f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"
667 )
668 r: "BaseRetrying"
669 sleep = dkw.get("sleep")
670 if _utils.is_coroutine_callable(f) or (
671 sleep is not None and _utils.is_coroutine_callable(sleep)
672 ):
673 r = AsyncRetrying(*dargs, **dkw)
674 elif (
675 tornado
676 and hasattr(tornado.gen, "is_coroutine_function")
677 and tornado.gen.is_coroutine_function(f)
678 ):
679 r = TornadoRetrying(*dargs, **dkw)
680 else:
681 r = Retrying(*dargs, **dkw)
683 return r.wraps(f)
685 return wrap
688from tenacity.asyncio import AsyncRetrying # noqa:E402,I100
690if tornado:
691 from tenacity.tornadoweb import TornadoRetrying
694__all__ = [
695 "retry_base",
696 "retry_all",
697 "retry_always",
698 "retry_any",
699 "retry_if_exception",
700 "retry_if_exception_type",
701 "retry_if_exception_cause_type",
702 "retry_if_not_exception_type",
703 "retry_if_not_result",
704 "retry_if_result",
705 "retry_never",
706 "retry_unless_exception_type",
707 "retry_if_exception_message",
708 "retry_if_not_exception_message",
709 "sleep",
710 "sleep_using_event",
711 "stop_after_attempt",
712 "stop_after_delay",
713 "stop_before_delay",
714 "stop_all",
715 "stop_any",
716 "stop_never",
717 "stop_when_event_set",
718 "wait_chain",
719 "wait_combine",
720 "wait_exception",
721 "wait_exponential",
722 "wait_fixed",
723 "wait_incrementing",
724 "wait_none",
725 "wait_random",
726 "wait_random_exponential",
727 "wait_full_jitter",
728 "wait_exponential_jitter",
729 "before_log",
730 "before_nothing",
731 "after_log",
732 "after_nothing",
733 "before_sleep_log",
734 "before_sleep_nothing",
735 "retry",
736 "WrappedFn",
737 "TryAgain",
738 "NO_RESULT",
739 "DoAttempt",
740 "DoSleep",
741 "BaseAction",
742 "RetryAction",
743 "RetryError",
744 "AttemptManager",
745 "BaseRetrying",
746 "Retrying",
747 "Future",
748 "RetryCallState",
749 "AsyncRetrying",
750]