|
16 | 16 | # See the License for the specific language governing permissions and
|
17 | 17 | # limitations under the License.
|
18 | 18 |
|
| 19 | + |
19 | 20 | import functools
|
20 | 21 | import sys
|
21 | 22 | import threading
|
|
91 | 92 | from .wait import WaitBaseT
|
92 | 93 |
|
93 | 94 |
|
| 95 | +WrappedFnReturnT = t.TypeVar("WrappedFnReturnT") |
94 | 96 | WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any])
|
95 |
| -_RetValT = t.TypeVar("_RetValT") |
96 |
| - |
97 |
| - |
98 |
| -def retry(*dargs: t.Any, **dkw: t.Any) -> t.Union[WrappedFn, t.Callable[[WrappedFn], WrappedFn]]: # noqa |
99 |
| - """Wrap a function with a new `Retrying` object. |
100 |
| -
|
101 |
| - :param dargs: positional arguments passed to Retrying object |
102 |
| - :param dkw: keyword arguments passed to the Retrying object |
103 |
| - """ |
104 |
| - # support both @retry and @retry() as valid syntax |
105 |
| - if len(dargs) == 1 and callable(dargs[0]): |
106 |
| - return retry()(dargs[0]) |
107 |
| - else: |
108 |
| - |
109 |
| - def wrap(f: WrappedFn) -> WrappedFn: |
110 |
| - if isinstance(f, retry_base): |
111 |
| - warnings.warn( |
112 |
| - f"Got retry_base instance ({f.__class__.__name__}) as callable argument, " |
113 |
| - f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)" |
114 |
| - ) |
115 |
| - if iscoroutinefunction(f): |
116 |
| - r: "BaseRetrying" = AsyncRetrying(*dargs, **dkw) |
117 |
| - elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f): |
118 |
| - r = TornadoRetrying(*dargs, **dkw) |
119 |
| - else: |
120 |
| - r = Retrying(*dargs, **dkw) |
121 |
| - |
122 |
| - return r.wraps(f) |
123 |
| - |
124 |
| - return wrap |
125 | 97 |
|
126 | 98 |
|
127 | 99 | class TryAgain(Exception):
|
@@ -382,14 +354,24 @@ def __iter__(self) -> t.Generator[AttemptManager, None, None]:
|
382 | 354 | break
|
383 | 355 |
|
384 | 356 | @abstractmethod
|
385 |
| - def __call__(self, fn: t.Callable[..., _RetValT], *args: t.Any, **kwargs: t.Any) -> _RetValT: |
| 357 | + def __call__( |
| 358 | + self, |
| 359 | + fn: t.Callable[..., WrappedFnReturnT], |
| 360 | + *args: t.Any, |
| 361 | + **kwargs: t.Any, |
| 362 | + ) -> WrappedFnReturnT: |
386 | 363 | pass
|
387 | 364 |
|
388 | 365 |
|
389 | 366 | class Retrying(BaseRetrying):
|
390 | 367 | """Retrying controller."""
|
391 | 368 |
|
392 |
| - def __call__(self, fn: t.Callable[..., _RetValT], *args: t.Any, **kwargs: t.Any) -> _RetValT: |
| 369 | + def __call__( |
| 370 | + self, |
| 371 | + fn: t.Callable[..., WrappedFnReturnT], |
| 372 | + *args: t.Any, |
| 373 | + **kwargs: t.Any, |
| 374 | + ) -> WrappedFnReturnT: |
393 | 375 | self.begin()
|
394 | 376 |
|
395 | 377 | retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
|
@@ -510,6 +492,57 @@ def __repr__(self) -> str:
|
510 | 492 | return f"<{clsname} {id(self)}: attempt #{self.attempt_number}; slept for {slept}; last result: {result}>"
|
511 | 493 |
|
512 | 494 |
|
| 495 | +@t.overload |
| 496 | +def retry(func: WrappedFn) -> WrappedFn: |
| 497 | + ... |
| 498 | + |
| 499 | + |
| 500 | +@t.overload |
| 501 | +def retry( |
| 502 | + sleep: t.Callable[[t.Union[int, float]], None] = sleep, |
| 503 | + stop: "StopBaseT" = stop_never, |
| 504 | + wait: "WaitBaseT" = wait_none(), |
| 505 | + retry: "RetryBaseT" = retry_if_exception_type(), |
| 506 | + before: t.Callable[["RetryCallState"], None] = before_nothing, |
| 507 | + after: t.Callable[["RetryCallState"], None] = after_nothing, |
| 508 | + before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None, |
| 509 | + reraise: bool = False, |
| 510 | + retry_error_cls: t.Type["RetryError"] = RetryError, |
| 511 | + retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None, |
| 512 | +) -> t.Callable[[WrappedFn], WrappedFn]: |
| 513 | + ... |
| 514 | + |
| 515 | + |
| 516 | +def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any: |
| 517 | + """Wrap a function with a new `Retrying` object. |
| 518 | +
|
| 519 | + :param dargs: positional arguments passed to Retrying object |
| 520 | + :param dkw: keyword arguments passed to the Retrying object |
| 521 | + """ |
| 522 | + # support both @retry and @retry() as valid syntax |
| 523 | + if len(dargs) == 1 and callable(dargs[0]): |
| 524 | + return retry()(dargs[0]) |
| 525 | + else: |
| 526 | + |
| 527 | + def wrap(f: WrappedFn) -> WrappedFn: |
| 528 | + if isinstance(f, retry_base): |
| 529 | + warnings.warn( |
| 530 | + f"Got retry_base instance ({f.__class__.__name__}) as callable argument, " |
| 531 | + f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)" |
| 532 | + ) |
| 533 | + r: "BaseRetrying" |
| 534 | + if iscoroutinefunction(f): |
| 535 | + r = AsyncRetrying(*dargs, **dkw) |
| 536 | + elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f): |
| 537 | + r = TornadoRetrying(*dargs, **dkw) |
| 538 | + else: |
| 539 | + r = Retrying(*dargs, **dkw) |
| 540 | + |
| 541 | + return r.wraps(f) |
| 542 | + |
| 543 | + return wrap |
| 544 | + |
| 545 | + |
513 | 546 | from tenacity._asyncio import AsyncRetrying # noqa:E402,I100
|
514 | 547 |
|
515 | 548 | if tornado:
|
|
0 commit comments