tenacity 8.3.0__py3-none-any.whl → 8.4.1__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
@@ -19,34 +19,87 @@ import functools
19
19
  import sys
20
20
  import typing as t
21
21
 
22
+ import tenacity
22
23
  from tenacity import AttemptManager
23
24
  from tenacity import BaseRetrying
24
25
  from tenacity import DoAttempt
25
26
  from tenacity import DoSleep
26
27
  from tenacity import RetryCallState
28
+ from tenacity import RetryError
29
+ from tenacity import after_nothing
30
+ from tenacity import before_nothing
27
31
  from tenacity import _utils
28
32
 
33
+ # Import all built-in retry strategies for easier usage.
34
+ from .retry import RetryBaseT
35
+ from .retry import retry_all # noqa
36
+ from .retry import retry_any # noqa
37
+ from .retry import retry_if_exception # noqa
38
+ from .retry import retry_if_result # noqa
39
+ from ..retry import RetryBaseT as SyncRetryBaseT
40
+
41
+ if t.TYPE_CHECKING:
42
+ from tenacity.stop import StopBaseT
43
+ from tenacity.wait import WaitBaseT
44
+
29
45
  WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
30
46
  WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Awaitable[t.Any]])
31
47
 
32
48
 
33
- def asyncio_sleep(duration: float) -> t.Awaitable[None]:
49
+ def _portable_async_sleep(seconds: float) -> t.Awaitable[None]:
50
+ # If trio is already imported, then importing it is cheap.
51
+ # If trio isn't already imported, then it's definitely not running, so we
52
+ # can skip further checks.
53
+ if "trio" in sys.modules:
54
+ # If trio is available, then sniffio is too
55
+ import trio
56
+ import sniffio
57
+
58
+ if sniffio.current_async_library() == "trio":
59
+ return trio.sleep(seconds)
60
+ # Otherwise, assume asyncio
34
61
  # Lazy import asyncio as it's expensive (responsible for 25-50% of total import overhead).
35
62
  import asyncio
36
63
 
37
- return asyncio.sleep(duration)
64
+ return asyncio.sleep(seconds)
38
65
 
39
66
 
40
67
  class AsyncRetrying(BaseRetrying):
41
- sleep: t.Callable[[float], t.Awaitable[t.Any]]
42
-
43
68
  def __init__(
44
69
  self,
45
- sleep: t.Callable[[float], t.Awaitable[t.Any]] = asyncio_sleep,
46
- **kwargs: t.Any,
70
+ sleep: t.Callable[
71
+ [t.Union[int, float]], t.Union[None, t.Awaitable[None]]
72
+ ] = _portable_async_sleep,
73
+ stop: "StopBaseT" = tenacity.stop.stop_never,
74
+ wait: "WaitBaseT" = tenacity.wait.wait_none(),
75
+ retry: "t.Union[SyncRetryBaseT, RetryBaseT]" = tenacity.retry_if_exception_type(),
76
+ before: t.Callable[
77
+ ["RetryCallState"], t.Union[None, t.Awaitable[None]]
78
+ ] = before_nothing,
79
+ after: t.Callable[
80
+ ["RetryCallState"], t.Union[None, t.Awaitable[None]]
81
+ ] = after_nothing,
82
+ before_sleep: t.Optional[
83
+ t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]]
84
+ ] = None,
85
+ reraise: bool = False,
86
+ retry_error_cls: t.Type["RetryError"] = RetryError,
87
+ retry_error_callback: t.Optional[
88
+ t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]]
89
+ ] = None,
47
90
  ) -> None:
48
- super().__init__(**kwargs)
49
- self.sleep = sleep
91
+ super().__init__(
92
+ sleep=sleep, # type: ignore[arg-type]
93
+ stop=stop,
94
+ wait=wait,
95
+ retry=retry, # type: ignore[arg-type]
96
+ before=before, # type: ignore[arg-type]
97
+ after=after, # type: ignore[arg-type]
98
+ before_sleep=before_sleep, # type: ignore[arg-type]
99
+ reraise=reraise,
100
+ retry_error_cls=retry_error_cls,
101
+ retry_error_callback=retry_error_callback,
102
+ )
50
103
 
51
104
  async def __call__( # type: ignore[override]
52
105
  self, fn: WrappedFn, *args: t.Any, **kwargs: t.Any
@@ -65,31 +118,21 @@ class AsyncRetrying(BaseRetrying):
65
118
  retry_state.set_result(result)
66
119
  elif isinstance(do, DoSleep):
67
120
  retry_state.prepare_for_next_attempt()
68
- await self.sleep(do)
121
+ await self.sleep(do) # type: ignore[misc]
69
122
  else:
70
123
  return do # type: ignore[no-any-return]
71
124
 
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
125
  def _add_action_func(self, fn: t.Callable[..., t.Any]) -> None:
83
- self.iter_state.actions.append(self._wrap_action_func(fn))
126
+ self.iter_state.actions.append(_utils.wrap_to_async_func(fn))
84
127
 
85
128
  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)(
129
+ self.iter_state.retry_run_result = await _utils.wrap_to_async_func(self.retry)(
87
130
  retry_state
88
131
  )
89
132
 
90
133
  async def _run_wait(self, retry_state: "RetryCallState") -> None: # type: ignore[override]
91
134
  if self.wait:
92
- sleep = await self._wrap_action_func(self.wait)(retry_state)
135
+ sleep = await _utils.wrap_to_async_func(self.wait)(retry_state)
93
136
  else:
94
137
  sleep = 0.0
95
138
 
@@ -97,7 +140,7 @@ class AsyncRetrying(BaseRetrying):
97
140
 
98
141
  async def _run_stop(self, retry_state: "RetryCallState") -> None: # type: ignore[override]
99
142
  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)(
143
+ self.iter_state.stop_run_result = await _utils.wrap_to_async_func(self.stop)(
101
144
  retry_state
102
145
  )
103
146
 
@@ -127,7 +170,7 @@ class AsyncRetrying(BaseRetrying):
127
170
  return AttemptManager(retry_state=self._retry_state)
128
171
  elif isinstance(do, DoSleep):
129
172
  self._retry_state.prepare_for_next_attempt()
130
- await self.sleep(do)
173
+ await self.sleep(do) # type: ignore[misc]
131
174
  else:
132
175
  raise StopAsyncIteration
133
176
 
@@ -146,3 +189,13 @@ class AsyncRetrying(BaseRetrying):
146
189
  async_wrapped.retry_with = fn.retry_with # type: ignore[attr-defined]
147
190
 
148
191
  return async_wrapped # type: ignore[return-value]
192
+
193
+
194
+ __all__ = [
195
+ "retry_all",
196
+ "retry_any",
197
+ "retry_if_exception",
198
+ "retry_if_result",
199
+ "WrappedFn",
200
+ "AsyncRetrying",
201
+ ]
@@ -0,0 +1,125 @@
1
+ # Copyright 2016–2021 Julien Danjou
2
+ # Copyright 2016 Joshua Harlow
3
+ # Copyright 2013-2014 Ray Holder
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ import abc
17
+ import typing
18
+
19
+ from tenacity import _utils
20
+ from tenacity import retry_base
21
+
22
+ if typing.TYPE_CHECKING:
23
+ from tenacity import RetryCallState
24
+
25
+
26
+ class async_retry_base(retry_base):
27
+ """Abstract base class for async retry strategies."""
28
+
29
+ @abc.abstractmethod
30
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
31
+ pass
32
+
33
+ def __and__( # type: ignore[override]
34
+ self, other: "typing.Union[retry_base, async_retry_base]"
35
+ ) -> "retry_all":
36
+ return retry_all(self, other)
37
+
38
+ def __rand__( # type: ignore[misc,override]
39
+ self, other: "typing.Union[retry_base, async_retry_base]"
40
+ ) -> "retry_all":
41
+ return retry_all(other, self)
42
+
43
+ def __or__( # type: ignore[override]
44
+ self, other: "typing.Union[retry_base, async_retry_base]"
45
+ ) -> "retry_any":
46
+ return retry_any(self, other)
47
+
48
+ def __ror__( # type: ignore[misc,override]
49
+ self, other: "typing.Union[retry_base, async_retry_base]"
50
+ ) -> "retry_any":
51
+ return retry_any(other, self)
52
+
53
+
54
+ RetryBaseT = typing.Union[
55
+ async_retry_base, typing.Callable[["RetryCallState"], typing.Awaitable[bool]]
56
+ ]
57
+
58
+
59
+ class retry_if_exception(async_retry_base):
60
+ """Retry strategy that retries if an exception verifies a predicate."""
61
+
62
+ def __init__(
63
+ self, predicate: typing.Callable[[BaseException], typing.Awaitable[bool]]
64
+ ) -> None:
65
+ self.predicate = predicate
66
+
67
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
68
+ if retry_state.outcome is None:
69
+ raise RuntimeError("__call__() called before outcome was set")
70
+
71
+ if retry_state.outcome.failed:
72
+ exception = retry_state.outcome.exception()
73
+ if exception is None:
74
+ raise RuntimeError("outcome failed but the exception is None")
75
+ return await self.predicate(exception)
76
+ else:
77
+ return False
78
+
79
+
80
+ class retry_if_result(async_retry_base):
81
+ """Retries if the result verifies a predicate."""
82
+
83
+ def __init__(
84
+ self, predicate: typing.Callable[[typing.Any], typing.Awaitable[bool]]
85
+ ) -> None:
86
+ self.predicate = predicate
87
+
88
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
89
+ if retry_state.outcome is None:
90
+ raise RuntimeError("__call__() called before outcome was set")
91
+
92
+ if not retry_state.outcome.failed:
93
+ return await self.predicate(retry_state.outcome.result())
94
+ else:
95
+ return False
96
+
97
+
98
+ class retry_any(async_retry_base):
99
+ """Retries if any of the retries condition is valid."""
100
+
101
+ def __init__(self, *retries: typing.Union[retry_base, async_retry_base]) -> None:
102
+ self.retries = retries
103
+
104
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
105
+ result = False
106
+ for r in self.retries:
107
+ result = result or await _utils.wrap_to_async_func(r)(retry_state)
108
+ if result:
109
+ break
110
+ return result
111
+
112
+
113
+ class retry_all(async_retry_base):
114
+ """Retries if all the retries condition are valid."""
115
+
116
+ def __init__(self, *retries: typing.Union[retry_base, async_retry_base]) -> None:
117
+ self.retries = retries
118
+
119
+ async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override]
120
+ result = True
121
+ for r in self.retries:
122
+ result = result and await _utils.wrap_to_async_func(r)(retry_state)
123
+ if not result:
124
+ break
125
+ return result
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.1
4
4
  Summary: Retry code until it succeeds
5
5
  Home-page: https://github.com/jd/tenacity
6
6
  Author: Julien Danjou
@@ -0,0 +1,18 @@
1
+ tenacity/__init__.py,sha256=dT216tDrncc6QKV6_1AfuhzmCMfUzM_hIwW34ivIRsQ,23651
2
+ tenacity/_utils.py,sha256=5AwPoFrGOIfPkCtqeJdFBi7KKQNcJXJ3erbtGOXtn6w,2916
3
+ tenacity/after.py,sha256=NR4rGyslG7xF1hDJZb2Wf8wVApafX0HZwz2nFVLvaqE,1658
4
+ tenacity/before.py,sha256=7zDTpZ3b6rkY9sOdS-qbpjBgSjVr3xBqcIqdYAh9ZKM,1544
5
+ tenacity/before_sleep.py,sha256=upKssiY5poOO3Bv0amADZA-a5CrY5tnb4_97s8_88SM,2360
6
+ tenacity/nap.py,sha256=fRWvnz1aIzbIq9Ap3gAkAZgDH6oo5zxMrU6ZOVByq0I,1383
7
+ tenacity/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ tenacity/retry.py,sha256=PVnZfXBeLN2DzsytKexw23SPLn82AdZfFPC2OhVQ_1Y,8986
9
+ tenacity/stop.py,sha256=wQuwGfCLw8OH1C3x0G9lH9xtJCyhgviePQ40HRAUg54,4113
10
+ tenacity/tornadoweb.py,sha256=vS1ONfPYoGzPx1asQaVbfoo6D9tPIzSysJipm61Yqw8,2125
11
+ tenacity/wait.py,sha256=Q9XoZCtFra53aQOyfABpvRDuUeB-NpUUXImHsUiRQI0,8042
12
+ tenacity/asyncio/__init__.py,sha256=i0GgKtsRWgKH2V1ESNsv9MY5rYvbN8ViRs3oe-UNWm8,7413
13
+ tenacity/asyncio/retry.py,sha256=ymu8F1JfAerc5vDO0V4-2kCvHXqyce2jpO1Nlt6aKxI,4244
14
+ tenacity-8.4.1.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
15
+ tenacity-8.4.1.dist-info/METADATA,sha256=mnX4jjfjt2yP61kwEC9cJVvuUUdB5COSlqE4HCge5Tg,1155
16
+ tenacity-8.4.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
17
+ tenacity-8.4.1.dist-info/top_level.txt,sha256=Zf8AOZMN7hr1EEcUo9U5KzXsM4TOC1pBZ22D8913JYs,9
18
+ tenacity-8.4.1.dist-info/RECORD,,
@@ -1,17 +0,0 @@
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
4
- tenacity/after.py,sha256=NR4rGyslG7xF1hDJZb2Wf8wVApafX0HZwz2nFVLvaqE,1658
5
- tenacity/before.py,sha256=7zDTpZ3b6rkY9sOdS-qbpjBgSjVr3xBqcIqdYAh9ZKM,1544
6
- tenacity/before_sleep.py,sha256=upKssiY5poOO3Bv0amADZA-a5CrY5tnb4_97s8_88SM,2360
7
- tenacity/nap.py,sha256=fRWvnz1aIzbIq9Ap3gAkAZgDH6oo5zxMrU6ZOVByq0I,1383
8
- tenacity/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- tenacity/retry.py,sha256=_Yb081RabODdc6FbI2Ipg8Cno4SpHKGao0i2jDvXPJQ,8794
10
- tenacity/stop.py,sha256=wQuwGfCLw8OH1C3x0G9lH9xtJCyhgviePQ40HRAUg54,4113
11
- tenacity/tornadoweb.py,sha256=vS1ONfPYoGzPx1asQaVbfoo6D9tPIzSysJipm61Yqw8,2125
12
- 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,,