tenacity 8.3.0__py3-none-any.whl → 8.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tenacity/__init__.py CHANGED
@@ -24,7 +24,8 @@ import typing as t
24
24
  import warnings
25
25
  from abc import ABC, abstractmethod
26
26
  from concurrent import futures
27
- from inspect import iscoroutinefunction
27
+
28
+ from . import _utils
28
29
 
29
30
  # Import all built-in retry strategies for easier usage.
30
31
  from .retry import retry_base # noqa
@@ -87,6 +88,7 @@ except ImportError:
87
88
  if t.TYPE_CHECKING:
88
89
  import types
89
90
 
91
+ from . import asyncio as tasyncio
90
92
  from .retry import RetryBaseT
91
93
  from .stop import StopBaseT
92
94
  from .wait import WaitBaseT
@@ -593,16 +595,24 @@ def retry(func: WrappedFn) -> WrappedFn: ...
593
595
 
594
596
  @t.overload
595
597
  def retry(
596
- sleep: t.Callable[[t.Union[int, float]], t.Optional[t.Awaitable[None]]] = sleep,
598
+ sleep: t.Callable[[t.Union[int, float]], t.Union[None, t.Awaitable[None]]] = sleep,
597
599
  stop: "StopBaseT" = stop_never,
598
600
  wait: "WaitBaseT" = wait_none(),
599
- retry: "RetryBaseT" = retry_if_exception_type(),
600
- before: t.Callable[["RetryCallState"], None] = before_nothing,
601
- after: t.Callable[["RetryCallState"], None] = after_nothing,
602
- before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
601
+ retry: "t.Union[RetryBaseT, tasyncio.retry.RetryBaseT]" = retry_if_exception_type(),
602
+ before: t.Callable[
603
+ ["RetryCallState"], t.Union[None, t.Awaitable[None]]
604
+ ] = before_nothing,
605
+ after: t.Callable[
606
+ ["RetryCallState"], t.Union[None, t.Awaitable[None]]
607
+ ] = after_nothing,
608
+ before_sleep: t.Optional[
609
+ t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]]
610
+ ] = None,
603
611
  reraise: bool = False,
604
612
  retry_error_cls: t.Type["RetryError"] = RetryError,
605
- retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None,
613
+ retry_error_callback: t.Optional[
614
+ t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]]
615
+ ] = None,
606
616
  ) -> t.Callable[[WrappedFn], WrappedFn]: ...
607
617
 
608
618
 
@@ -624,7 +634,7 @@ def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any:
624
634
  f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"
625
635
  )
626
636
  r: "BaseRetrying"
627
- if iscoroutinefunction(f):
637
+ if _utils.is_coroutine_callable(f):
628
638
  r = AsyncRetrying(*dargs, **dkw)
629
639
  elif (
630
640
  tornado
@@ -640,7 +650,7 @@ def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any:
640
650
  return wrap
641
651
 
642
652
 
643
- from tenacity._asyncio import AsyncRetrying # noqa:E402,I100
653
+ from tenacity.asyncio import AsyncRetrying # noqa:E402,I100
644
654
 
645
655
  if tornado:
646
656
  from tenacity.tornadoweb import TornadoRetrying
tenacity/_utils.py CHANGED
@@ -87,3 +87,15 @@ def is_coroutine_callable(call: typing.Callable[..., typing.Any]) -> bool:
87
87
  partial_call = isinstance(call, functools.partial) and call.func
88
88
  dunder_call = partial_call or getattr(call, "__call__", None)
89
89
  return inspect.iscoroutinefunction(dunder_call)
90
+
91
+
92
+ def wrap_to_async_func(
93
+ call: typing.Callable[..., typing.Any],
94
+ ) -> typing.Callable[..., typing.Awaitable[typing.Any]]:
95
+ if is_coroutine_callable(call):
96
+ return call
97
+
98
+ async def inner(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
99
+ return call(*args, **kwargs)
100
+
101
+ return inner
tenacity/retry.py CHANGED
@@ -30,10 +30,16 @@ class retry_base(abc.ABC):
30
30
  pass
31
31
 
32
32
  def __and__(self, other: "retry_base") -> "retry_all":
33
- return retry_all(self, other)
33
+ return other.__rand__(self)
34
+
35
+ def __rand__(self, other: "retry_base") -> "retry_all":
36
+ return retry_all(other, self)
34
37
 
35
38
  def __or__(self, other: "retry_base") -> "retry_any":
36
- return retry_any(self, other)
39
+ return other.__ror__(self)
40
+
41
+ def __ror__(self, other: "retry_base") -> "retry_any":
42
+ return retry_any(other, self)
37
43
 
38
44
 
39
45
  RetryBaseT = typing.Union[retry_base, typing.Callable[["RetryCallState"], bool]]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tenacity
3
- Version: 8.3.0
3
+ Version: 8.4.0
4
4
  Summary: Retry code until it succeeds
5
5
  Home-page: https://github.com/jd/tenacity
6
6
  Author: Julien Danjou
@@ -1,17 +1,16 @@
1
- tenacity/__init__.py,sha256=pXkuWY13-Ual7lJs3t2GfFBaFY8Epq6ipLxA5TA4c04,23415
2
- tenacity/_asyncio.py,sha256=AcnW-qYS1Lf9d7P_Y9FMd7hyh6bJlQU1C073sIbZ6U4,5369
3
- tenacity/_utils.py,sha256=GGNfSHJRrfph7kPySn-2xnY_dVUopdl6LUZdXRPuGVg,2601
1
+ tenacity/__init__.py,sha256=dT216tDrncc6QKV6_1AfuhzmCMfUzM_hIwW34ivIRsQ,23651
2
+ tenacity/_utils.py,sha256=5AwPoFrGOIfPkCtqeJdFBi7KKQNcJXJ3erbtGOXtn6w,2916
4
3
  tenacity/after.py,sha256=NR4rGyslG7xF1hDJZb2Wf8wVApafX0HZwz2nFVLvaqE,1658
5
4
  tenacity/before.py,sha256=7zDTpZ3b6rkY9sOdS-qbpjBgSjVr3xBqcIqdYAh9ZKM,1544
6
5
  tenacity/before_sleep.py,sha256=upKssiY5poOO3Bv0amADZA-a5CrY5tnb4_97s8_88SM,2360
7
6
  tenacity/nap.py,sha256=fRWvnz1aIzbIq9Ap3gAkAZgDH6oo5zxMrU6ZOVByq0I,1383
8
7
  tenacity/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- tenacity/retry.py,sha256=_Yb081RabODdc6FbI2Ipg8Cno4SpHKGao0i2jDvXPJQ,8794
8
+ tenacity/retry.py,sha256=PVnZfXBeLN2DzsytKexw23SPLn82AdZfFPC2OhVQ_1Y,8986
10
9
  tenacity/stop.py,sha256=wQuwGfCLw8OH1C3x0G9lH9xtJCyhgviePQ40HRAUg54,4113
11
10
  tenacity/tornadoweb.py,sha256=vS1ONfPYoGzPx1asQaVbfoo6D9tPIzSysJipm61Yqw8,2125
12
11
  tenacity/wait.py,sha256=Q9XoZCtFra53aQOyfABpvRDuUeB-NpUUXImHsUiRQI0,8042
13
- tenacity-8.3.0.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
14
- tenacity-8.3.0.dist-info/METADATA,sha256=EkN7WPJ0qwEErJNURA8THEGpBSKbYrpT5SEZMQkH7yk,1155
15
- tenacity-8.3.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
16
- tenacity-8.3.0.dist-info/top_level.txt,sha256=Zf8AOZMN7hr1EEcUo9U5KzXsM4TOC1pBZ22D8913JYs,9
17
- tenacity-8.3.0.dist-info/RECORD,,
12
+ tenacity-8.4.0.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
13
+ tenacity-8.4.0.dist-info/METADATA,sha256=rnsKmqyLFhyN5Ydqjehhxuls6kPQ27yu6M4l2t5WeuM,1155
14
+ tenacity-8.4.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
15
+ tenacity-8.4.0.dist-info/top_level.txt,sha256=Zf8AOZMN7hr1EEcUo9U5KzXsM4TOC1pBZ22D8913JYs,9
16
+ tenacity-8.4.0.dist-info/RECORD,,
tenacity/_asyncio.py DELETED
@@ -1,148 +0,0 @@
1
- # Copyright 2016 Étienne Bersac
2
- # Copyright 2016 Julien Danjou
3
- # Copyright 2016 Joshua Harlow
4
- # Copyright 2013-2014 Ray Holder
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
-
18
- import functools
19
- import sys
20
- import typing as t
21
-
22
- from tenacity import AttemptManager
23
- from tenacity import BaseRetrying
24
- from tenacity import DoAttempt
25
- from tenacity import DoSleep
26
- from tenacity import RetryCallState
27
- from tenacity import _utils
28
-
29
- WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
30
- WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Awaitable[t.Any]])
31
-
32
-
33
- def asyncio_sleep(duration: float) -> t.Awaitable[None]:
34
- # Lazy import asyncio as it's expensive (responsible for 25-50% of total import overhead).
35
- import asyncio
36
-
37
- return asyncio.sleep(duration)
38
-
39
-
40
- class AsyncRetrying(BaseRetrying):
41
- sleep: t.Callable[[float], t.Awaitable[t.Any]]
42
-
43
- def __init__(
44
- self,
45
- sleep: t.Callable[[float], t.Awaitable[t.Any]] = asyncio_sleep,
46
- **kwargs: t.Any,
47
- ) -> None:
48
- super().__init__(**kwargs)
49
- self.sleep = sleep
50
-
51
- async def __call__( # type: ignore[override]
52
- self, fn: WrappedFn, *args: t.Any, **kwargs: t.Any
53
- ) -> WrappedFnReturnT:
54
- self.begin()
55
-
56
- retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
57
- while True:
58
- do = await self.iter(retry_state=retry_state)
59
- if isinstance(do, DoAttempt):
60
- try:
61
- result = await fn(*args, **kwargs)
62
- except BaseException: # noqa: B902
63
- retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
64
- else:
65
- retry_state.set_result(result)
66
- elif isinstance(do, DoSleep):
67
- retry_state.prepare_for_next_attempt()
68
- await self.sleep(do)
69
- else:
70
- return do # type: ignore[no-any-return]
71
-
72
- @classmethod
73
- def _wrap_action_func(cls, fn: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:
74
- if _utils.is_coroutine_callable(fn):
75
- return fn
76
-
77
- async def inner(*args: t.Any, **kwargs: t.Any) -> t.Any:
78
- return fn(*args, **kwargs)
79
-
80
- return inner
81
-
82
- def _add_action_func(self, fn: t.Callable[..., t.Any]) -> None:
83
- self.iter_state.actions.append(self._wrap_action_func(fn))
84
-
85
- async def _run_retry(self, retry_state: "RetryCallState") -> None: # type: ignore[override]
86
- self.iter_state.retry_run_result = await self._wrap_action_func(self.retry)(
87
- retry_state
88
- )
89
-
90
- async def _run_wait(self, retry_state: "RetryCallState") -> None: # type: ignore[override]
91
- if self.wait:
92
- sleep = await self._wrap_action_func(self.wait)(retry_state)
93
- else:
94
- sleep = 0.0
95
-
96
- retry_state.upcoming_sleep = sleep
97
-
98
- async def _run_stop(self, retry_state: "RetryCallState") -> None: # type: ignore[override]
99
- self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start
100
- self.iter_state.stop_run_result = await self._wrap_action_func(self.stop)(
101
- retry_state
102
- )
103
-
104
- async def iter(
105
- self, retry_state: "RetryCallState"
106
- ) -> t.Union[DoAttempt, DoSleep, t.Any]: # noqa: A003
107
- self._begin_iter(retry_state)
108
- result = None
109
- for action in self.iter_state.actions:
110
- result = await action(retry_state)
111
- return result
112
-
113
- def __iter__(self) -> t.Generator[AttemptManager, None, None]:
114
- raise TypeError("AsyncRetrying object is not iterable")
115
-
116
- def __aiter__(self) -> "AsyncRetrying":
117
- self.begin()
118
- self._retry_state = RetryCallState(self, fn=None, args=(), kwargs={})
119
- return self
120
-
121
- async def __anext__(self) -> AttemptManager:
122
- while True:
123
- do = await self.iter(retry_state=self._retry_state)
124
- if do is None:
125
- raise StopAsyncIteration
126
- elif isinstance(do, DoAttempt):
127
- return AttemptManager(retry_state=self._retry_state)
128
- elif isinstance(do, DoSleep):
129
- self._retry_state.prepare_for_next_attempt()
130
- await self.sleep(do)
131
- else:
132
- raise StopAsyncIteration
133
-
134
- def wraps(self, fn: WrappedFn) -> WrappedFn:
135
- fn = super().wraps(fn)
136
- # Ensure wrapper is recognized as a coroutine function.
137
-
138
- @functools.wraps(
139
- fn, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")
140
- )
141
- async def async_wrapped(*args: t.Any, **kwargs: t.Any) -> t.Any:
142
- return await fn(*args, **kwargs)
143
-
144
- # Preserve attributes
145
- async_wrapped.retry = fn.retry # type: ignore[attr-defined]
146
- async_wrapped.retry_with = fn.retry_with # type: ignore[attr-defined]
147
-
148
- return async_wrapped # type: ignore[return-value]