__init__.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  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.
  18. import dataclasses
  19. import functools
  20. import sys
  21. import threading
  22. import time
  23. import typing as t
  24. import warnings
  25. from abc import ABC, abstractmethod
  26. from concurrent import futures
  27. from . import _utils
  28. # Import all built-in retry strategies for easier usage.
  29. from .retry import retry_base # noqa
  30. from .retry import retry_all # noqa
  31. from .retry import retry_always # noqa
  32. from .retry import retry_any # noqa
  33. from .retry import retry_if_exception # noqa
  34. from .retry import retry_if_exception_type # noqa
  35. from .retry import retry_if_exception_cause_type # noqa
  36. from .retry import retry_if_not_exception_type # noqa
  37. from .retry import retry_if_not_result # noqa
  38. from .retry import retry_if_result # noqa
  39. from .retry import retry_never # noqa
  40. from .retry import retry_unless_exception_type # noqa
  41. from .retry import retry_if_exception_message # noqa
  42. from .retry import retry_if_not_exception_message # noqa
  43. # Import all nap strategies for easier usage.
  44. from .nap import sleep # noqa
  45. from .nap import sleep_using_event # noqa
  46. # Import all built-in stop strategies for easier usage.
  47. from .stop import stop_after_attempt # noqa
  48. from .stop import stop_after_delay # noqa
  49. from .stop import stop_before_delay # noqa
  50. from .stop import stop_all # noqa
  51. from .stop import stop_any # noqa
  52. from .stop import stop_never # noqa
  53. from .stop import stop_when_event_set # noqa
  54. # Import all built-in wait strategies for easier usage.
  55. from .wait import wait_chain # noqa
  56. from .wait import wait_combine # noqa
  57. from .wait import wait_exponential # noqa
  58. from .wait import wait_fixed # noqa
  59. from .wait import wait_incrementing # noqa
  60. from .wait import wait_none # noqa
  61. from .wait import wait_random # noqa
  62. from .wait import wait_random_exponential # noqa
  63. from .wait import wait_random_exponential as wait_full_jitter # noqa
  64. from .wait import wait_exponential_jitter # noqa
  65. # Import all built-in before strategies for easier usage.
  66. from .before import before_log # noqa
  67. from .before import before_nothing # noqa
  68. # Import all built-in after strategies for easier usage.
  69. from .after import after_log # noqa
  70. from .after import after_nothing # noqa
  71. # Import all built-in before sleep strategies for easier usage.
  72. from .before_sleep import before_sleep_log # noqa
  73. from .before_sleep import before_sleep_nothing # noqa
  74. try:
  75. import tornado
  76. except ImportError:
  77. tornado = None
  78. if t.TYPE_CHECKING:
  79. import types
  80. from typing_extensions import Self
  81. from . import asyncio as tasyncio
  82. from .retry import RetryBaseT
  83. from .stop import StopBaseT
  84. from .wait import WaitBaseT
  85. WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
  86. WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any])
  87. dataclass_kwargs = {}
  88. if sys.version_info >= (3, 10):
  89. dataclass_kwargs.update({"slots": True})
  90. @dataclasses.dataclass(**dataclass_kwargs)
  91. class IterState:
  92. actions: t.List[t.Callable[["RetryCallState"], t.Any]] = dataclasses.field(
  93. default_factory=list
  94. )
  95. retry_run_result: bool = False
  96. delay_since_first_attempt: int = 0
  97. stop_run_result: bool = False
  98. is_explicit_retry: bool = False
  99. def reset(self) -> None:
  100. self.actions = []
  101. self.retry_run_result = False
  102. self.delay_since_first_attempt = 0
  103. self.stop_run_result = False
  104. self.is_explicit_retry = False
  105. class TryAgain(Exception):
  106. """Always retry the executed function when raised."""
  107. NO_RESULT = object()
  108. class DoAttempt:
  109. pass
  110. class DoSleep(float):
  111. pass
  112. class BaseAction:
  113. """Base class for representing actions to take by retry object.
  114. Concrete implementations must define:
  115. - __init__: to initialize all necessary fields
  116. - REPR_FIELDS: class variable specifying attributes to include in repr(self)
  117. - NAME: for identification in retry object methods and callbacks
  118. """
  119. REPR_FIELDS: t.Sequence[str] = ()
  120. NAME: t.Optional[str] = None
  121. def __repr__(self) -> str:
  122. state_str = ", ".join(
  123. f"{field}={getattr(self, field)!r}" for field in self.REPR_FIELDS
  124. )
  125. return f"{self.__class__.__name__}({state_str})"
  126. def __str__(self) -> str:
  127. return repr(self)
  128. class RetryAction(BaseAction):
  129. REPR_FIELDS = ("sleep",)
  130. NAME = "retry"
  131. def __init__(self, sleep: t.SupportsFloat) -> None:
  132. self.sleep = float(sleep)
  133. _unset = object()
  134. def _first_set(first: t.Union[t.Any, object], second: t.Any) -> t.Any:
  135. return second if first is _unset else first
  136. class RetryError(Exception):
  137. """Encapsulates the last attempt instance right before giving up."""
  138. def __init__(self, last_attempt: "Future") -> None:
  139. self.last_attempt = last_attempt
  140. super().__init__(last_attempt)
  141. def reraise(self) -> t.NoReturn:
  142. if self.last_attempt.failed:
  143. raise self.last_attempt.result()
  144. raise self
  145. def __str__(self) -> str:
  146. return f"{self.__class__.__name__}[{self.last_attempt}]"
  147. class AttemptManager:
  148. """Manage attempt context."""
  149. def __init__(self, retry_state: "RetryCallState"):
  150. self.retry_state = retry_state
  151. def __enter__(self) -> None:
  152. pass
  153. def __exit__(
  154. self,
  155. exc_type: t.Optional[t.Type[BaseException]],
  156. exc_value: t.Optional[BaseException],
  157. traceback: t.Optional["types.TracebackType"],
  158. ) -> t.Optional[bool]:
  159. if exc_type is not None and exc_value is not None:
  160. self.retry_state.set_exception((exc_type, exc_value, traceback))
  161. return True # Swallow exception.
  162. else:
  163. # We don't have the result, actually.
  164. self.retry_state.set_result(None)
  165. return None
  166. class BaseRetrying(ABC):
  167. def __init__(
  168. self,
  169. sleep: t.Callable[[t.Union[int, float]], None] = sleep,
  170. stop: "StopBaseT" = stop_never,
  171. wait: "WaitBaseT" = wait_none(),
  172. retry: "RetryBaseT" = retry_if_exception_type(),
  173. before: t.Callable[["RetryCallState"], None] = before_nothing,
  174. after: t.Callable[["RetryCallState"], None] = after_nothing,
  175. before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
  176. reraise: bool = False,
  177. retry_error_cls: t.Type[RetryError] = RetryError,
  178. retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None,
  179. ):
  180. self.sleep = sleep
  181. self.stop = stop
  182. self.wait = wait
  183. self.retry = retry
  184. self.before = before
  185. self.after = after
  186. self.before_sleep = before_sleep
  187. self.reraise = reraise
  188. self._local = threading.local()
  189. self.retry_error_cls = retry_error_cls
  190. self.retry_error_callback = retry_error_callback
  191. def copy(
  192. self,
  193. sleep: t.Union[t.Callable[[t.Union[int, float]], None], object] = _unset,
  194. stop: t.Union["StopBaseT", object] = _unset,
  195. wait: t.Union["WaitBaseT", object] = _unset,
  196. retry: t.Union[retry_base, object] = _unset,
  197. before: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
  198. after: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
  199. before_sleep: t.Union[
  200. t.Optional[t.Callable[["RetryCallState"], None]], object
  201. ] = _unset,
  202. reraise: t.Union[bool, object] = _unset,
  203. retry_error_cls: t.Union[t.Type[RetryError], object] = _unset,
  204. retry_error_callback: t.Union[
  205. t.Optional[t.Callable[["RetryCallState"], t.Any]], object
  206. ] = _unset,
  207. ) -> "Self":
  208. """Copy this object with some parameters changed if needed."""
  209. return self.__class__(
  210. sleep=_first_set(sleep, self.sleep),
  211. stop=_first_set(stop, self.stop),
  212. wait=_first_set(wait, self.wait),
  213. retry=_first_set(retry, self.retry),
  214. before=_first_set(before, self.before),
  215. after=_first_set(after, self.after),
  216. before_sleep=_first_set(before_sleep, self.before_sleep),
  217. reraise=_first_set(reraise, self.reraise),
  218. retry_error_cls=_first_set(retry_error_cls, self.retry_error_cls),
  219. retry_error_callback=_first_set(
  220. retry_error_callback, self.retry_error_callback
  221. ),
  222. )
  223. def __repr__(self) -> str:
  224. return (
  225. f"<{self.__class__.__name__} object at 0x{id(self):x} ("
  226. f"stop={self.stop}, "
  227. f"wait={self.wait}, "
  228. f"sleep={self.sleep}, "
  229. f"retry={self.retry}, "
  230. f"before={self.before}, "
  231. f"after={self.after})>"
  232. )
  233. @property
  234. def statistics(self) -> t.Dict[str, t.Any]:
  235. """Return a dictionary of runtime statistics.
  236. This dictionary will be empty when the controller has never been
  237. ran. When it is running or has ran previously it should have (but
  238. may not) have useful and/or informational keys and values when
  239. running is underway and/or completed.
  240. .. warning:: The keys in this dictionary **should** be some what
  241. stable (not changing), but there existence **may**
  242. change between major releases as new statistics are
  243. gathered or removed so before accessing keys ensure that
  244. they actually exist and handle when they do not.
  245. .. note:: The values in this dictionary are local to the thread
  246. running call (so if multiple threads share the same retrying
  247. object - either directly or indirectly) they will each have
  248. there own view of statistics they have collected (in the
  249. future we may provide a way to aggregate the various
  250. statistics from each thread).
  251. """
  252. try:
  253. return self._local.statistics # type: ignore[no-any-return]
  254. except AttributeError:
  255. self._local.statistics = t.cast(t.Dict[str, t.Any], {})
  256. return self._local.statistics
  257. @property
  258. def iter_state(self) -> IterState:
  259. try:
  260. return self._local.iter_state # type: ignore[no-any-return]
  261. except AttributeError:
  262. self._local.iter_state = IterState()
  263. return self._local.iter_state
  264. def wraps(self, f: WrappedFn) -> WrappedFn:
  265. """Wrap a function for retrying.
  266. :param f: A function to wraps for retrying.
  267. """
  268. @functools.wraps(
  269. f, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")
  270. )
  271. def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any:
  272. # Always create a copy to prevent overwriting the local contexts when
  273. # calling the same wrapped functions multiple times in the same stack
  274. copy = self.copy()
  275. wrapped_f.statistics = copy.statistics # type: ignore[attr-defined]
  276. return copy(f, *args, **kw)
  277. def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:
  278. return self.copy(*args, **kwargs).wraps(f)
  279. # Preserve attributes
  280. wrapped_f.retry = self # type: ignore[attr-defined]
  281. wrapped_f.retry_with = retry_with # type: ignore[attr-defined]
  282. wrapped_f.statistics = {} # type: ignore[attr-defined]
  283. return wrapped_f # type: ignore[return-value]
  284. def begin(self) -> None:
  285. self.statistics.clear()
  286. self.statistics["start_time"] = time.monotonic()
  287. self.statistics["attempt_number"] = 1
  288. self.statistics["idle_for"] = 0
  289. def _add_action_func(self, fn: t.Callable[..., t.Any]) -> None:
  290. self.iter_state.actions.append(fn)
  291. def _run_retry(self, retry_state: "RetryCallState") -> None:
  292. self.iter_state.retry_run_result = self.retry(retry_state)
  293. def _run_wait(self, retry_state: "RetryCallState") -> None:
  294. if self.wait:
  295. sleep = self.wait(retry_state)
  296. else:
  297. sleep = 0.0
  298. retry_state.upcoming_sleep = sleep
  299. def _run_stop(self, retry_state: "RetryCallState") -> None:
  300. self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start
  301. self.iter_state.stop_run_result = self.stop(retry_state)
  302. def iter(self, retry_state: "RetryCallState") -> t.Union[DoAttempt, DoSleep, t.Any]: # noqa
  303. self._begin_iter(retry_state)
  304. result = None
  305. for action in self.iter_state.actions:
  306. result = action(retry_state)
  307. return result
  308. def _begin_iter(self, retry_state: "RetryCallState") -> None: # noqa
  309. self.iter_state.reset()
  310. fut = retry_state.outcome
  311. if fut is None:
  312. if self.before is not None:
  313. self._add_action_func(self.before)
  314. self._add_action_func(lambda rs: DoAttempt())
  315. return
  316. self.iter_state.is_explicit_retry = fut.failed and isinstance(
  317. fut.exception(), TryAgain
  318. )
  319. if not self.iter_state.is_explicit_retry:
  320. self._add_action_func(self._run_retry)
  321. self._add_action_func(self._post_retry_check_actions)
  322. def _post_retry_check_actions(self, retry_state: "RetryCallState") -> None:
  323. if not (self.iter_state.is_explicit_retry or self.iter_state.retry_run_result):
  324. self._add_action_func(lambda rs: rs.outcome.result())
  325. return
  326. if self.after is not None:
  327. self._add_action_func(self.after)
  328. self._add_action_func(self._run_wait)
  329. self._add_action_func(self._run_stop)
  330. self._add_action_func(self._post_stop_check_actions)
  331. def _post_stop_check_actions(self, retry_state: "RetryCallState") -> None:
  332. if self.iter_state.stop_run_result:
  333. if self.retry_error_callback:
  334. self._add_action_func(self.retry_error_callback)
  335. return
  336. def exc_check(rs: "RetryCallState") -> None:
  337. fut = t.cast(Future, rs.outcome)
  338. retry_exc = self.retry_error_cls(fut)
  339. if self.reraise:
  340. raise retry_exc.reraise()
  341. raise retry_exc from fut.exception()
  342. self._add_action_func(exc_check)
  343. return
  344. def next_action(rs: "RetryCallState") -> None:
  345. sleep = rs.upcoming_sleep
  346. rs.next_action = RetryAction(sleep)
  347. rs.idle_for += sleep
  348. self.statistics["idle_for"] += sleep
  349. self.statistics["attempt_number"] += 1
  350. self._add_action_func(next_action)
  351. if self.before_sleep is not None:
  352. self._add_action_func(self.before_sleep)
  353. self._add_action_func(lambda rs: DoSleep(rs.upcoming_sleep))
  354. def __iter__(self) -> t.Generator[AttemptManager, None, None]:
  355. self.begin()
  356. retry_state = RetryCallState(self, fn=None, args=(), kwargs={})
  357. while True:
  358. do = self.iter(retry_state=retry_state)
  359. if isinstance(do, DoAttempt):
  360. yield AttemptManager(retry_state=retry_state)
  361. elif isinstance(do, DoSleep):
  362. retry_state.prepare_for_next_attempt()
  363. self.sleep(do)
  364. else:
  365. break
  366. @abstractmethod
  367. def __call__(
  368. self,
  369. fn: t.Callable[..., WrappedFnReturnT],
  370. *args: t.Any,
  371. **kwargs: t.Any,
  372. ) -> WrappedFnReturnT:
  373. pass
  374. class Retrying(BaseRetrying):
  375. """Retrying controller."""
  376. def __call__(
  377. self,
  378. fn: t.Callable[..., WrappedFnReturnT],
  379. *args: t.Any,
  380. **kwargs: t.Any,
  381. ) -> WrappedFnReturnT:
  382. self.begin()
  383. retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
  384. while True:
  385. do = self.iter(retry_state=retry_state)
  386. if isinstance(do, DoAttempt):
  387. try:
  388. result = fn(*args, **kwargs)
  389. except BaseException: # noqa: B902
  390. retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
  391. else:
  392. retry_state.set_result(result)
  393. elif isinstance(do, DoSleep):
  394. retry_state.prepare_for_next_attempt()
  395. self.sleep(do)
  396. else:
  397. return do # type: ignore[no-any-return]
  398. if sys.version_info >= (3, 9):
  399. FutureGenericT = futures.Future[t.Any]
  400. else:
  401. FutureGenericT = futures.Future
  402. class Future(FutureGenericT):
  403. """Encapsulates a (future or past) attempted call to a target function."""
  404. def __init__(self, attempt_number: int) -> None:
  405. super().__init__()
  406. self.attempt_number = attempt_number
  407. @property
  408. def failed(self) -> bool:
  409. """Return whether a exception is being held in this future."""
  410. return self.exception() is not None
  411. @classmethod
  412. def construct(
  413. cls, attempt_number: int, value: t.Any, has_exception: bool
  414. ) -> "Future":
  415. """Construct a new Future object."""
  416. fut = cls(attempt_number)
  417. if has_exception:
  418. fut.set_exception(value)
  419. else:
  420. fut.set_result(value)
  421. return fut
  422. class RetryCallState:
  423. """State related to a single call wrapped with Retrying."""
  424. def __init__(
  425. self,
  426. retry_object: BaseRetrying,
  427. fn: t.Optional[WrappedFn],
  428. args: t.Any,
  429. kwargs: t.Any,
  430. ) -> None:
  431. #: Retry call start timestamp
  432. self.start_time = time.monotonic()
  433. #: Retry manager object
  434. self.retry_object = retry_object
  435. #: Function wrapped by this retry call
  436. self.fn = fn
  437. #: Arguments of the function wrapped by this retry call
  438. self.args = args
  439. #: Keyword arguments of the function wrapped by this retry call
  440. self.kwargs = kwargs
  441. #: The number of the current attempt
  442. self.attempt_number: int = 1
  443. #: Last outcome (result or exception) produced by the function
  444. self.outcome: t.Optional[Future] = None
  445. #: Timestamp of the last outcome
  446. self.outcome_timestamp: t.Optional[float] = None
  447. #: Time spent sleeping in retries
  448. self.idle_for: float = 0.0
  449. #: Next action as decided by the retry manager
  450. self.next_action: t.Optional[RetryAction] = None
  451. #: Next sleep time as decided by the retry manager.
  452. self.upcoming_sleep: float = 0.0
  453. @property
  454. def seconds_since_start(self) -> t.Optional[float]:
  455. if self.outcome_timestamp is None:
  456. return None
  457. return self.outcome_timestamp - self.start_time
  458. def prepare_for_next_attempt(self) -> None:
  459. self.outcome = None
  460. self.outcome_timestamp = None
  461. self.attempt_number += 1
  462. self.next_action = None
  463. def set_result(self, val: t.Any) -> None:
  464. ts = time.monotonic()
  465. fut = Future(self.attempt_number)
  466. fut.set_result(val)
  467. self.outcome, self.outcome_timestamp = fut, ts
  468. def set_exception(
  469. self,
  470. exc_info: t.Tuple[
  471. t.Type[BaseException], BaseException, "types.TracebackType| None"
  472. ],
  473. ) -> None:
  474. ts = time.monotonic()
  475. fut = Future(self.attempt_number)
  476. fut.set_exception(exc_info[1])
  477. self.outcome, self.outcome_timestamp = fut, ts
  478. def __repr__(self) -> str:
  479. if self.outcome is None:
  480. result = "none yet"
  481. elif self.outcome.failed:
  482. exception = self.outcome.exception()
  483. result = f"failed ({exception.__class__.__name__} {exception})"
  484. else:
  485. result = f"returned {self.outcome.result()}"
  486. slept = float(round(self.idle_for, 2))
  487. clsname = self.__class__.__name__
  488. return f"<{clsname} {id(self)}: attempt #{self.attempt_number}; slept for {slept}; last result: {result}>"
  489. @t.overload
  490. def retry(func: WrappedFn) -> WrappedFn: ...
  491. @t.overload
  492. def retry(
  493. sleep: t.Callable[[t.Union[int, float]], t.Union[None, t.Awaitable[None]]] = sleep,
  494. stop: "StopBaseT" = stop_never,
  495. wait: "WaitBaseT" = wait_none(),
  496. retry: "t.Union[RetryBaseT, tasyncio.retry.RetryBaseT]" = retry_if_exception_type(),
  497. before: t.Callable[
  498. ["RetryCallState"], t.Union[None, t.Awaitable[None]]
  499. ] = before_nothing,
  500. after: t.Callable[
  501. ["RetryCallState"], t.Union[None, t.Awaitable[None]]
  502. ] = after_nothing,
  503. before_sleep: t.Optional[
  504. t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]]
  505. ] = None,
  506. reraise: bool = False,
  507. retry_error_cls: t.Type["RetryError"] = RetryError,
  508. retry_error_callback: t.Optional[
  509. t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]]
  510. ] = None,
  511. ) -> t.Callable[[WrappedFn], WrappedFn]: ...
  512. def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any:
  513. """Wrap a function with a new `Retrying` object.
  514. :param dargs: positional arguments passed to Retrying object
  515. :param dkw: keyword arguments passed to the Retrying object
  516. """
  517. # support both @retry and @retry() as valid syntax
  518. if len(dargs) == 1 and callable(dargs[0]):
  519. return retry()(dargs[0])
  520. else:
  521. def wrap(f: WrappedFn) -> WrappedFn:
  522. if isinstance(f, retry_base):
  523. warnings.warn(
  524. f"Got retry_base instance ({f.__class__.__name__}) as callable argument, "
  525. f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"
  526. )
  527. r: "BaseRetrying"
  528. if _utils.is_coroutine_callable(f):
  529. r = AsyncRetrying(*dargs, **dkw)
  530. elif (
  531. tornado
  532. and hasattr(tornado.gen, "is_coroutine_function")
  533. and tornado.gen.is_coroutine_function(f)
  534. ):
  535. r = TornadoRetrying(*dargs, **dkw)
  536. else:
  537. r = Retrying(*dargs, **dkw)
  538. return r.wraps(f)
  539. return wrap
  540. from tenacity.asyncio import AsyncRetrying # noqa:E402,I100
  541. if tornado:
  542. from tenacity.tornadoweb import TornadoRetrying
  543. __all__ = [
  544. "retry_base",
  545. "retry_all",
  546. "retry_always",
  547. "retry_any",
  548. "retry_if_exception",
  549. "retry_if_exception_type",
  550. "retry_if_exception_cause_type",
  551. "retry_if_not_exception_type",
  552. "retry_if_not_result",
  553. "retry_if_result",
  554. "retry_never",
  555. "retry_unless_exception_type",
  556. "retry_if_exception_message",
  557. "retry_if_not_exception_message",
  558. "sleep",
  559. "sleep_using_event",
  560. "stop_after_attempt",
  561. "stop_after_delay",
  562. "stop_before_delay",
  563. "stop_all",
  564. "stop_any",
  565. "stop_never",
  566. "stop_when_event_set",
  567. "wait_chain",
  568. "wait_combine",
  569. "wait_exponential",
  570. "wait_fixed",
  571. "wait_incrementing",
  572. "wait_none",
  573. "wait_random",
  574. "wait_random_exponential",
  575. "wait_full_jitter",
  576. "wait_exponential_jitter",
  577. "before_log",
  578. "before_nothing",
  579. "after_log",
  580. "after_nothing",
  581. "before_sleep_log",
  582. "before_sleep_nothing",
  583. "retry",
  584. "WrappedFn",
  585. "TryAgain",
  586. "NO_RESULT",
  587. "DoAttempt",
  588. "DoSleep",
  589. "BaseAction",
  590. "RetryAction",
  591. "RetryError",
  592. "AttemptManager",
  593. "BaseRetrying",
  594. "Retrying",
  595. "Future",
  596. "RetryCallState",
  597. "AsyncRetrying",
  598. ]