Change venv
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import functools
|
||||
import sys
|
||||
import threading
|
||||
@@ -33,6 +34,7 @@ from .retry import retry_always # noqa
|
||||
from .retry import retry_any # noqa
|
||||
from .retry import retry_if_exception # noqa
|
||||
from .retry import retry_if_exception_type # noqa
|
||||
from .retry import retry_if_exception_cause_type # noqa
|
||||
from .retry import retry_if_not_exception_type # noqa
|
||||
from .retry import retry_if_not_result # noqa
|
||||
from .retry import retry_if_result # noqa
|
||||
@@ -63,6 +65,7 @@ from .wait import wait_none # noqa
|
||||
from .wait import wait_random # noqa
|
||||
from .wait import wait_random_exponential # noqa
|
||||
from .wait import wait_random_exponential as wait_full_jitter # noqa
|
||||
from .wait import wait_exponential_jitter # noqa
|
||||
|
||||
# Import all built-in before strategies for easier usage.
|
||||
from .before import before_log # noqa
|
||||
@@ -86,51 +89,13 @@ tornado = None # type: ignore
|
||||
if t.TYPE_CHECKING:
|
||||
import types
|
||||
|
||||
from .wait import wait_base
|
||||
from .stop import stop_base
|
||||
from .retry import RetryBaseT
|
||||
from .stop import StopBaseT
|
||||
from .wait import WaitBaseT
|
||||
|
||||
|
||||
WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable)
|
||||
_RetValT = t.TypeVar("_RetValT")
|
||||
|
||||
|
||||
@t.overload
|
||||
def retry(fn: WrappedFn) -> WrappedFn:
|
||||
pass
|
||||
|
||||
|
||||
@t.overload
|
||||
def retry(*dargs: t.Any, **dkw: t.Any) -> t.Callable[[WrappedFn], WrappedFn]: # noqa
|
||||
pass
|
||||
|
||||
|
||||
def retry(*dargs: t.Any, **dkw: t.Any) -> t.Union[WrappedFn, t.Callable[[WrappedFn], WrappedFn]]: # noqa
|
||||
"""Wrap a function with a new `Retrying` object.
|
||||
|
||||
:param dargs: positional arguments passed to Retrying object
|
||||
:param dkw: keyword arguments passed to the Retrying object
|
||||
"""
|
||||
# support both @retry and @retry() as valid syntax
|
||||
if len(dargs) == 1 and callable(dargs[0]):
|
||||
return retry()(dargs[0])
|
||||
else:
|
||||
|
||||
def wrap(f: WrappedFn) -> WrappedFn:
|
||||
if isinstance(f, retry_base):
|
||||
warnings.warn(
|
||||
f"Got retry_base instance ({f.__class__.__name__}) as callable argument, "
|
||||
f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"
|
||||
)
|
||||
if iscoroutinefunction(f):
|
||||
r: "BaseRetrying" = AsyncRetrying(*dargs, **dkw)
|
||||
elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f):
|
||||
r = TornadoRetrying(*dargs, **dkw)
|
||||
else:
|
||||
r = Retrying(*dargs, **dkw)
|
||||
|
||||
return r.wraps(f)
|
||||
|
||||
return wrap
|
||||
WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
|
||||
WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any])
|
||||
|
||||
|
||||
class TryAgain(Exception):
|
||||
@@ -214,7 +179,7 @@ class AttemptManager:
|
||||
exc_value: t.Optional[BaseException],
|
||||
traceback: t.Optional["types.TracebackType"],
|
||||
) -> t.Optional[bool]:
|
||||
if isinstance(exc_value, BaseException):
|
||||
if exc_type is not None and exc_value is not None:
|
||||
self.retry_state.set_exception((exc_type, exc_value, traceback))
|
||||
return True # Swallow exception.
|
||||
else:
|
||||
@@ -227,9 +192,9 @@ class BaseRetrying(ABC):
|
||||
def __init__(
|
||||
self,
|
||||
sleep: t.Callable[[t.Union[int, float]], None] = sleep,
|
||||
stop: "stop_base" = stop_never,
|
||||
wait: "wait_base" = wait_none(),
|
||||
retry: retry_base = retry_if_exception_type(),
|
||||
stop: "StopBaseT" = stop_never,
|
||||
wait: "WaitBaseT" = wait_none(),
|
||||
retry: "RetryBaseT" = retry_if_exception_type(),
|
||||
before: t.Callable[["RetryCallState"], None] = before_nothing,
|
||||
after: t.Callable[["RetryCallState"], None] = after_nothing,
|
||||
before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
|
||||
@@ -252,8 +217,8 @@ class BaseRetrying(ABC):
|
||||
def copy(
|
||||
self,
|
||||
sleep: t.Union[t.Callable[[t.Union[int, float]], None], object] = _unset,
|
||||
stop: t.Union["stop_base", object] = _unset,
|
||||
wait: t.Union["wait_base", object] = _unset,
|
||||
stop: t.Union["StopBaseT", object] = _unset,
|
||||
wait: t.Union["WaitBaseT", object] = _unset,
|
||||
retry: t.Union[retry_base, object] = _unset,
|
||||
before: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
|
||||
after: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,
|
||||
@@ -310,9 +275,9 @@ class BaseRetrying(ABC):
|
||||
statistics from each thread).
|
||||
"""
|
||||
try:
|
||||
return self._local.statistics
|
||||
return self._local.statistics # type: ignore[no-any-return]
|
||||
except AttributeError:
|
||||
self._local.statistics = {}
|
||||
self._local.statistics = t.cast(t.Dict[str, t.Any], {})
|
||||
return self._local.statistics
|
||||
|
||||
def wraps(self, f: WrappedFn) -> WrappedFn:
|
||||
@@ -328,10 +293,10 @@ class BaseRetrying(ABC):
|
||||
def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:
|
||||
return self.copy(*args, **kwargs).wraps(f)
|
||||
|
||||
wrapped_f.retry = self
|
||||
wrapped_f.retry_with = retry_with
|
||||
wrapped_f.retry = self # type: ignore[attr-defined]
|
||||
wrapped_f.retry_with = retry_with # type: ignore[attr-defined]
|
||||
|
||||
return wrapped_f
|
||||
return wrapped_f # type: ignore[return-value]
|
||||
|
||||
def begin(self) -> None:
|
||||
self.statistics.clear()
|
||||
@@ -346,15 +311,15 @@ class BaseRetrying(ABC):
|
||||
self.before(retry_state)
|
||||
return DoAttempt()
|
||||
|
||||
is_explicit_retry = retry_state.outcome.failed and isinstance(retry_state.outcome.exception(), TryAgain)
|
||||
if not (is_explicit_retry or self.retry(retry_state=retry_state)):
|
||||
is_explicit_retry = fut.failed and isinstance(fut.exception(), TryAgain)
|
||||
if not (is_explicit_retry or self.retry(retry_state)):
|
||||
return fut.result()
|
||||
|
||||
if self.after is not None:
|
||||
self.after(retry_state)
|
||||
|
||||
self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start
|
||||
if self.stop(retry_state=retry_state):
|
||||
if self.stop(retry_state):
|
||||
if self.retry_error_callback:
|
||||
return self.retry_error_callback(retry_state)
|
||||
retry_exc = self.retry_error_cls(fut)
|
||||
@@ -363,7 +328,7 @@ class BaseRetrying(ABC):
|
||||
raise retry_exc from fut.exception()
|
||||
|
||||
if self.wait:
|
||||
sleep = self.wait(retry_state=retry_state)
|
||||
sleep = self.wait(retry_state)
|
||||
else:
|
||||
sleep = 0.0
|
||||
retry_state.next_action = RetryAction(sleep)
|
||||
@@ -391,14 +356,24 @@ class BaseRetrying(ABC):
|
||||
break
|
||||
|
||||
@abstractmethod
|
||||
def __call__(self, fn: t.Callable[..., _RetValT], *args: t.Any, **kwargs: t.Any) -> _RetValT:
|
||||
def __call__(
|
||||
self,
|
||||
fn: t.Callable[..., WrappedFnReturnT],
|
||||
*args: t.Any,
|
||||
**kwargs: t.Any,
|
||||
) -> WrappedFnReturnT:
|
||||
pass
|
||||
|
||||
|
||||
class Retrying(BaseRetrying):
|
||||
"""Retrying controller."""
|
||||
|
||||
def __call__(self, fn: t.Callable[..., _RetValT], *args: t.Any, **kwargs: t.Any) -> _RetValT:
|
||||
def __call__(
|
||||
self,
|
||||
fn: t.Callable[..., WrappedFnReturnT],
|
||||
*args: t.Any,
|
||||
**kwargs: t.Any,
|
||||
) -> WrappedFnReturnT:
|
||||
self.begin()
|
||||
|
||||
retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
|
||||
@@ -408,17 +383,23 @@ class Retrying(BaseRetrying):
|
||||
try:
|
||||
result = fn(*args, **kwargs)
|
||||
except BaseException: # noqa: B902
|
||||
retry_state.set_exception(sys.exc_info())
|
||||
retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
|
||||
else:
|
||||
retry_state.set_result(result)
|
||||
elif isinstance(do, DoSleep):
|
||||
retry_state.prepare_for_next_attempt()
|
||||
self.sleep(do)
|
||||
else:
|
||||
return do
|
||||
return do # type: ignore[no-any-return]
|
||||
|
||||
|
||||
class Future(futures.Future):
|
||||
if sys.version_info[1] >= 9:
|
||||
FutureGenericT = futures.Future[t.Any]
|
||||
else:
|
||||
FutureGenericT = futures.Future
|
||||
|
||||
|
||||
class Future(FutureGenericT):
|
||||
"""Encapsulates a (future or past) attempted call to a target function."""
|
||||
|
||||
def __init__(self, attempt_number: int) -> None:
|
||||
@@ -491,13 +472,15 @@ class RetryCallState:
|
||||
fut.set_result(val)
|
||||
self.outcome, self.outcome_timestamp = fut, ts
|
||||
|
||||
def set_exception(self, exc_info: t.Tuple[t.Type[BaseException], BaseException, "types.TracebackType"]) -> None:
|
||||
def set_exception(
|
||||
self, exc_info: t.Tuple[t.Type[BaseException], BaseException, "types.TracebackType| None"]
|
||||
) -> None:
|
||||
ts = time.monotonic()
|
||||
fut = Future(self.attempt_number)
|
||||
fut.set_exception(exc_info[1])
|
||||
self.outcome, self.outcome_timestamp = fut, ts
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
if self.outcome is None:
|
||||
result = "none yet"
|
||||
elif self.outcome.failed:
|
||||
@@ -511,7 +494,115 @@ class RetryCallState:
|
||||
return f"<{clsname} {id(self)}: attempt #{self.attempt_number}; slept for {slept}; last result: {result}>"
|
||||
|
||||
|
||||
@t.overload
|
||||
def retry(func: WrappedFn) -> WrappedFn:
|
||||
...
|
||||
|
||||
|
||||
@t.overload
|
||||
def retry(
|
||||
sleep: t.Callable[[t.Union[int, float]], None] = sleep,
|
||||
stop: "StopBaseT" = stop_never,
|
||||
wait: "WaitBaseT" = wait_none(),
|
||||
retry: "RetryBaseT" = retry_if_exception_type(),
|
||||
before: t.Callable[["RetryCallState"], None] = before_nothing,
|
||||
after: t.Callable[["RetryCallState"], None] = after_nothing,
|
||||
before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,
|
||||
reraise: bool = False,
|
||||
retry_error_cls: t.Type["RetryError"] = RetryError,
|
||||
retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None,
|
||||
) -> t.Callable[[WrappedFn], WrappedFn]:
|
||||
...
|
||||
|
||||
|
||||
def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any:
|
||||
"""Wrap a function with a new `Retrying` object.
|
||||
|
||||
:param dargs: positional arguments passed to Retrying object
|
||||
:param dkw: keyword arguments passed to the Retrying object
|
||||
"""
|
||||
# support both @retry and @retry() as valid syntax
|
||||
if len(dargs) == 1 and callable(dargs[0]):
|
||||
return retry()(dargs[0])
|
||||
else:
|
||||
|
||||
def wrap(f: WrappedFn) -> WrappedFn:
|
||||
if isinstance(f, retry_base):
|
||||
warnings.warn(
|
||||
f"Got retry_base instance ({f.__class__.__name__}) as callable argument, "
|
||||
f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"
|
||||
)
|
||||
r: "BaseRetrying"
|
||||
if iscoroutinefunction(f):
|
||||
r = AsyncRetrying(*dargs, **dkw)
|
||||
elif tornado and hasattr(tornado.gen, "is_coroutine_function") and tornado.gen.is_coroutine_function(f):
|
||||
r = TornadoRetrying(*dargs, **dkw)
|
||||
else:
|
||||
r = Retrying(*dargs, **dkw)
|
||||
|
||||
return r.wraps(f)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
from pip._vendor.tenacity._asyncio import AsyncRetrying # noqa:E402,I100
|
||||
|
||||
if tornado:
|
||||
from pip._vendor.tenacity.tornadoweb import TornadoRetrying
|
||||
|
||||
|
||||
__all__ = [
|
||||
"retry_base",
|
||||
"retry_all",
|
||||
"retry_always",
|
||||
"retry_any",
|
||||
"retry_if_exception",
|
||||
"retry_if_exception_type",
|
||||
"retry_if_exception_cause_type",
|
||||
"retry_if_not_exception_type",
|
||||
"retry_if_not_result",
|
||||
"retry_if_result",
|
||||
"retry_never",
|
||||
"retry_unless_exception_type",
|
||||
"retry_if_exception_message",
|
||||
"retry_if_not_exception_message",
|
||||
"sleep",
|
||||
"sleep_using_event",
|
||||
"stop_after_attempt",
|
||||
"stop_after_delay",
|
||||
"stop_all",
|
||||
"stop_any",
|
||||
"stop_never",
|
||||
"stop_when_event_set",
|
||||
"wait_chain",
|
||||
"wait_combine",
|
||||
"wait_exponential",
|
||||
"wait_fixed",
|
||||
"wait_incrementing",
|
||||
"wait_none",
|
||||
"wait_random",
|
||||
"wait_random_exponential",
|
||||
"wait_full_jitter",
|
||||
"wait_exponential_jitter",
|
||||
"before_log",
|
||||
"before_nothing",
|
||||
"after_log",
|
||||
"after_nothing",
|
||||
"before_sleep_log",
|
||||
"before_sleep_nothing",
|
||||
"retry",
|
||||
"WrappedFn",
|
||||
"TryAgain",
|
||||
"NO_RESULT",
|
||||
"DoAttempt",
|
||||
"DoSleep",
|
||||
"BaseAction",
|
||||
"RetryAction",
|
||||
"RetryError",
|
||||
"AttemptManager",
|
||||
"BaseRetrying",
|
||||
"Retrying",
|
||||
"Future",
|
||||
"RetryCallState",
|
||||
"AsyncRetrying",
|
||||
]
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
import functools
|
||||
import sys
|
||||
import typing
|
||||
import typing as t
|
||||
from asyncio import sleep
|
||||
|
||||
from pip._vendor.tenacity import AttemptManager
|
||||
@@ -26,21 +26,20 @@ from pip._vendor.tenacity import DoAttempt
|
||||
from pip._vendor.tenacity import DoSleep
|
||||
from pip._vendor.tenacity import RetryCallState
|
||||
|
||||
WrappedFn = typing.TypeVar("WrappedFn", bound=typing.Callable)
|
||||
_RetValT = typing.TypeVar("_RetValT")
|
||||
WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
|
||||
WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Awaitable[t.Any]])
|
||||
|
||||
|
||||
class AsyncRetrying(BaseRetrying):
|
||||
def __init__(self, sleep: typing.Callable[[float], typing.Awaitable] = sleep, **kwargs: typing.Any) -> None:
|
||||
sleep: t.Callable[[float], t.Awaitable[t.Any]]
|
||||
|
||||
def __init__(self, sleep: t.Callable[[float], t.Awaitable[t.Any]] = sleep, **kwargs: t.Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.sleep = sleep
|
||||
|
||||
async def __call__( # type: ignore # Change signature from supertype
|
||||
self,
|
||||
fn: typing.Callable[..., typing.Awaitable[_RetValT]],
|
||||
*args: typing.Any,
|
||||
**kwargs: typing.Any,
|
||||
) -> _RetValT:
|
||||
async def __call__( # type: ignore[override]
|
||||
self, fn: WrappedFn, *args: t.Any, **kwargs: t.Any
|
||||
) -> WrappedFnReturnT:
|
||||
self.begin()
|
||||
|
||||
retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
|
||||
@@ -50,21 +49,24 @@ class AsyncRetrying(BaseRetrying):
|
||||
try:
|
||||
result = await fn(*args, **kwargs)
|
||||
except BaseException: # noqa: B902
|
||||
retry_state.set_exception(sys.exc_info())
|
||||
retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
|
||||
else:
|
||||
retry_state.set_result(result)
|
||||
elif isinstance(do, DoSleep):
|
||||
retry_state.prepare_for_next_attempt()
|
||||
await self.sleep(do)
|
||||
else:
|
||||
return do
|
||||
return do # type: ignore[no-any-return]
|
||||
|
||||
def __iter__(self) -> t.Generator[AttemptManager, None, None]:
|
||||
raise TypeError("AsyncRetrying object is not iterable")
|
||||
|
||||
def __aiter__(self) -> "AsyncRetrying":
|
||||
self.begin()
|
||||
self._retry_state = RetryCallState(self, fn=None, args=(), kwargs={})
|
||||
return self
|
||||
|
||||
async def __anext__(self) -> typing.Union[AttemptManager, typing.Any]:
|
||||
async def __anext__(self) -> AttemptManager:
|
||||
while True:
|
||||
do = self.iter(retry_state=self._retry_state)
|
||||
if do is None:
|
||||
@@ -75,18 +77,18 @@ class AsyncRetrying(BaseRetrying):
|
||||
self._retry_state.prepare_for_next_attempt()
|
||||
await self.sleep(do)
|
||||
else:
|
||||
return do
|
||||
raise StopAsyncIteration
|
||||
|
||||
def wraps(self, fn: WrappedFn) -> WrappedFn:
|
||||
fn = super().wraps(fn)
|
||||
# Ensure wrapper is recognized as a coroutine function.
|
||||
|
||||
@functools.wraps(fn)
|
||||
async def async_wrapped(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
||||
async def async_wrapped(*args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||
return await fn(*args, **kwargs)
|
||||
|
||||
# Preserve attributes
|
||||
async_wrapped.retry = fn.retry
|
||||
async_wrapped.retry_with = fn.retry_with
|
||||
async_wrapped.retry = fn.retry # type: ignore[attr-defined]
|
||||
async_wrapped.retry_with = fn.retry_with # type: ignore[attr-defined]
|
||||
|
||||
return async_wrapped
|
||||
return async_wrapped # type: ignore[return-value]
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
import sys
|
||||
import typing
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
# sys.maxsize:
|
||||
@@ -66,3 +67,10 @@ def get_callback_name(cb: typing.Callable[..., typing.Any]) -> str:
|
||||
except AttributeError:
|
||||
pass
|
||||
return ".".join(segments)
|
||||
|
||||
|
||||
time_unit_type = typing.Union[int, float, timedelta]
|
||||
|
||||
|
||||
def to_seconds(time_unit: time_unit_type) -> float:
|
||||
return float(time_unit.total_seconds() if isinstance(time_unit, timedelta) else time_unit)
|
||||
|
@@ -36,9 +36,14 @@ def after_log(
|
||||
"""After call strategy that logs to some logger the finished attempt."""
|
||||
|
||||
def log_it(retry_state: "RetryCallState") -> None:
|
||||
if retry_state.fn is None:
|
||||
# NOTE(sileht): can't really happen, but we must please mypy
|
||||
fn_name = "<unknown>"
|
||||
else:
|
||||
fn_name = _utils.get_callback_name(retry_state.fn)
|
||||
logger.log(
|
||||
log_level,
|
||||
f"Finished call to '{_utils.get_callback_name(retry_state.fn)}' "
|
||||
f"Finished call to '{fn_name}' "
|
||||
f"after {sec_format % retry_state.seconds_since_start}(s), "
|
||||
f"this was the {_utils.to_ordinal(retry_state.attempt_number)} time calling it.",
|
||||
)
|
||||
|
@@ -32,9 +32,14 @@ def before_log(logger: "logging.Logger", log_level: int) -> typing.Callable[["Re
|
||||
"""Before call strategy that logs to some logger the attempt."""
|
||||
|
||||
def log_it(retry_state: "RetryCallState") -> None:
|
||||
if retry_state.fn is None:
|
||||
# NOTE(sileht): can't really happen, but we must please mypy
|
||||
fn_name = "<unknown>"
|
||||
else:
|
||||
fn_name = _utils.get_callback_name(retry_state.fn)
|
||||
logger.log(
|
||||
log_level,
|
||||
f"Starting call to '{_utils.get_callback_name(retry_state.fn)}', "
|
||||
f"Starting call to '{fn_name}', "
|
||||
f"this is the {_utils.to_ordinal(retry_state.attempt_number)} time calling it.",
|
||||
)
|
||||
|
||||
|
@@ -36,6 +36,14 @@ def before_sleep_log(
|
||||
"""Before call strategy that logs to some logger the attempt."""
|
||||
|
||||
def log_it(retry_state: "RetryCallState") -> None:
|
||||
local_exc_info: BaseException | bool | None
|
||||
|
||||
if retry_state.outcome is None:
|
||||
raise RuntimeError("log_it() called before outcome was set")
|
||||
|
||||
if retry_state.next_action is None:
|
||||
raise RuntimeError("log_it() called before next_action was set")
|
||||
|
||||
if retry_state.outcome.failed:
|
||||
ex = retry_state.outcome.exception()
|
||||
verb, value = "raised", f"{ex.__class__.__name__}: {ex}"
|
||||
@@ -48,10 +56,15 @@ def before_sleep_log(
|
||||
verb, value = "returned", retry_state.outcome.result()
|
||||
local_exc_info = False # exc_info does not apply when no exception
|
||||
|
||||
if retry_state.fn is None:
|
||||
# NOTE(sileht): can't really happen, but we must please mypy
|
||||
fn_name = "<unknown>"
|
||||
else:
|
||||
fn_name = _utils.get_callback_name(retry_state.fn)
|
||||
|
||||
logger.log(
|
||||
log_level,
|
||||
f"Retrying {_utils.get_callback_name(retry_state.fn)} "
|
||||
f"in {retry_state.next_action.sleep} seconds as it {verb} {value}.",
|
||||
f"Retrying {fn_name} " f"in {retry_state.next_action.sleep} seconds as it {verb} {value}.",
|
||||
exc_info=local_exc_info,
|
||||
)
|
||||
|
||||
|
@@ -36,6 +36,9 @@ class retry_base(abc.ABC):
|
||||
return retry_any(self, other)
|
||||
|
||||
|
||||
RetryBaseT = typing.Union[retry_base, typing.Callable[["RetryCallState"], bool]]
|
||||
|
||||
|
||||
class _retry_never(retry_base):
|
||||
"""Retry strategy that never rejects any result."""
|
||||
|
||||
@@ -63,8 +66,14 @@ class retry_if_exception(retry_base):
|
||||
self.predicate = predicate
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> bool:
|
||||
if retry_state.outcome is None:
|
||||
raise RuntimeError("__call__() called before outcome was set")
|
||||
|
||||
if retry_state.outcome.failed:
|
||||
return self.predicate(retry_state.outcome.exception())
|
||||
exception = retry_state.outcome.exception()
|
||||
if exception is None:
|
||||
raise RuntimeError("outcome failed but the exception is None")
|
||||
return self.predicate(exception)
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -111,10 +120,47 @@ class retry_unless_exception_type(retry_if_exception):
|
||||
super().__init__(lambda e: not isinstance(e, exception_types))
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> bool:
|
||||
if retry_state.outcome is None:
|
||||
raise RuntimeError("__call__() called before outcome was set")
|
||||
|
||||
# always retry if no exception was raised
|
||||
if not retry_state.outcome.failed:
|
||||
return True
|
||||
return self.predicate(retry_state.outcome.exception())
|
||||
|
||||
exception = retry_state.outcome.exception()
|
||||
if exception is None:
|
||||
raise RuntimeError("outcome failed but the exception is None")
|
||||
return self.predicate(exception)
|
||||
|
||||
|
||||
class retry_if_exception_cause_type(retry_base):
|
||||
"""Retries if any of the causes of the raised exception is of one or more types.
|
||||
|
||||
The check on the type of the cause of the exception is done recursively (until finding
|
||||
an exception in the chain that has no `__cause__`)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
exception_types: typing.Union[
|
||||
typing.Type[BaseException],
|
||||
typing.Tuple[typing.Type[BaseException], ...],
|
||||
] = Exception,
|
||||
) -> None:
|
||||
self.exception_cause_types = exception_types
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> bool:
|
||||
if retry_state.outcome is None:
|
||||
raise RuntimeError("__call__ called before outcome was set")
|
||||
|
||||
if retry_state.outcome.failed:
|
||||
exc = retry_state.outcome.exception()
|
||||
while exc is not None:
|
||||
if isinstance(exc.__cause__, self.exception_cause_types):
|
||||
return True
|
||||
exc = exc.__cause__
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class retry_if_result(retry_base):
|
||||
@@ -124,6 +170,9 @@ class retry_if_result(retry_base):
|
||||
self.predicate = predicate
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> bool:
|
||||
if retry_state.outcome is None:
|
||||
raise RuntimeError("__call__() called before outcome was set")
|
||||
|
||||
if not retry_state.outcome.failed:
|
||||
return self.predicate(retry_state.outcome.result())
|
||||
else:
|
||||
@@ -137,6 +186,9 @@ class retry_if_not_result(retry_base):
|
||||
self.predicate = predicate
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> bool:
|
||||
if retry_state.outcome is None:
|
||||
raise RuntimeError("__call__() called before outcome was set")
|
||||
|
||||
if not retry_state.outcome.failed:
|
||||
return not self.predicate(retry_state.outcome.result())
|
||||
else:
|
||||
@@ -188,9 +240,16 @@ class retry_if_not_exception_message(retry_if_exception_message):
|
||||
self.predicate = lambda *args_, **kwargs_: not if_predicate(*args_, **kwargs_)
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> bool:
|
||||
if retry_state.outcome is None:
|
||||
raise RuntimeError("__call__() called before outcome was set")
|
||||
|
||||
if not retry_state.outcome.failed:
|
||||
return True
|
||||
return self.predicate(retry_state.outcome.exception())
|
||||
|
||||
exception = retry_state.outcome.exception()
|
||||
if exception is None:
|
||||
raise RuntimeError("outcome failed but the exception is None")
|
||||
return self.predicate(exception)
|
||||
|
||||
|
||||
class retry_any(retry_base):
|
||||
|
@@ -16,6 +16,8 @@
|
||||
import abc
|
||||
import typing
|
||||
|
||||
from pip._vendor.tenacity import _utils
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
import threading
|
||||
|
||||
@@ -36,6 +38,9 @@ class stop_base(abc.ABC):
|
||||
return stop_any(self, other)
|
||||
|
||||
|
||||
StopBaseT = typing.Union[stop_base, typing.Callable[["RetryCallState"], bool]]
|
||||
|
||||
|
||||
class stop_any(stop_base):
|
||||
"""Stop if any of the stop condition is valid."""
|
||||
|
||||
@@ -89,8 +94,10 @@ class stop_after_attempt(stop_base):
|
||||
class stop_after_delay(stop_base):
|
||||
"""Stop when the time from the first attempt >= limit."""
|
||||
|
||||
def __init__(self, max_delay: float) -> None:
|
||||
self.max_delay = max_delay
|
||||
def __init__(self, max_delay: _utils.time_unit_type) -> None:
|
||||
self.max_delay = _utils.to_seconds(max_delay)
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> bool:
|
||||
if retry_state.seconds_since_start is None:
|
||||
raise RuntimeError("__call__() called but seconds_since_start is not set")
|
||||
return retry_state.seconds_since_start >= self.max_delay
|
||||
|
@@ -33,8 +33,8 @@ class TornadoRetrying(BaseRetrying):
|
||||
super().__init__(**kwargs)
|
||||
self.sleep = sleep
|
||||
|
||||
@gen.coroutine
|
||||
def __call__( # type: ignore # Change signature from supertype
|
||||
@gen.coroutine # type: ignore[misc]
|
||||
def __call__(
|
||||
self,
|
||||
fn: "typing.Callable[..., typing.Union[typing.Generator[typing.Any, typing.Any, _RetValT], Future[_RetValT]]]",
|
||||
*args: typing.Any,
|
||||
@@ -49,7 +49,7 @@ class TornadoRetrying(BaseRetrying):
|
||||
try:
|
||||
result = yield fn(*args, **kwargs)
|
||||
except BaseException: # noqa: B902
|
||||
retry_state.set_exception(sys.exc_info())
|
||||
retry_state.set_exception(sys.exc_info()) # type: ignore[arg-type]
|
||||
else:
|
||||
retry_state.set_result(result)
|
||||
elif isinstance(do, DoSleep):
|
||||
|
@@ -36,16 +36,19 @@ class wait_base(abc.ABC):
|
||||
|
||||
def __radd__(self, other: "wait_base") -> typing.Union["wait_combine", "wait_base"]:
|
||||
# make it possible to use multiple waits with the built-in sum function
|
||||
if other == 0:
|
||||
if other == 0: # type: ignore[comparison-overlap]
|
||||
return self
|
||||
return self.__add__(other)
|
||||
|
||||
|
||||
WaitBaseT = typing.Union[wait_base, typing.Callable[["RetryCallState"], typing.Union[float, int]]]
|
||||
|
||||
|
||||
class wait_fixed(wait_base):
|
||||
"""Wait strategy that waits a fixed amount of time between each retry."""
|
||||
|
||||
def __init__(self, wait: float) -> None:
|
||||
self.wait_fixed = wait
|
||||
def __init__(self, wait: _utils.time_unit_type) -> None:
|
||||
self.wait_fixed = _utils.to_seconds(wait)
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> float:
|
||||
return self.wait_fixed
|
||||
@@ -61,9 +64,9 @@ class wait_none(wait_fixed):
|
||||
class wait_random(wait_base):
|
||||
"""Wait strategy that waits a random amount of time between min/max."""
|
||||
|
||||
def __init__(self, min: typing.Union[int, float] = 0, max: typing.Union[int, float] = 1) -> None: # noqa
|
||||
self.wait_random_min = min
|
||||
self.wait_random_max = max
|
||||
def __init__(self, min: _utils.time_unit_type = 0, max: _utils.time_unit_type = 1) -> None: # noqa
|
||||
self.wait_random_min = _utils.to_seconds(min)
|
||||
self.wait_random_max = _utils.to_seconds(max)
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> float:
|
||||
return self.wait_random_min + (random.random() * (self.wait_random_max - self.wait_random_min))
|
||||
@@ -113,13 +116,13 @@ class wait_incrementing(wait_base):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
start: typing.Union[int, float] = 0,
|
||||
increment: typing.Union[int, float] = 100,
|
||||
max: typing.Union[int, float] = _utils.MAX_WAIT, # noqa
|
||||
start: _utils.time_unit_type = 0,
|
||||
increment: _utils.time_unit_type = 100,
|
||||
max: _utils.time_unit_type = _utils.MAX_WAIT, # noqa
|
||||
) -> None:
|
||||
self.start = start
|
||||
self.increment = increment
|
||||
self.max = max
|
||||
self.start = _utils.to_seconds(start)
|
||||
self.increment = _utils.to_seconds(increment)
|
||||
self.max = _utils.to_seconds(max)
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> float:
|
||||
result = self.start + (self.increment * (retry_state.attempt_number - 1))
|
||||
@@ -142,13 +145,13 @@ class wait_exponential(wait_base):
|
||||
def __init__(
|
||||
self,
|
||||
multiplier: typing.Union[int, float] = 1,
|
||||
max: typing.Union[int, float] = _utils.MAX_WAIT, # noqa
|
||||
max: _utils.time_unit_type = _utils.MAX_WAIT, # noqa
|
||||
exp_base: typing.Union[int, float] = 2,
|
||||
min: typing.Union[int, float] = 0, # noqa
|
||||
min: _utils.time_unit_type = 0, # noqa
|
||||
) -> None:
|
||||
self.multiplier = multiplier
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.min = _utils.to_seconds(min)
|
||||
self.max = _utils.to_seconds(max)
|
||||
self.exp_base = exp_base
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> float:
|
||||
@@ -189,3 +192,37 @@ class wait_random_exponential(wait_exponential):
|
||||
def __call__(self, retry_state: "RetryCallState") -> float:
|
||||
high = super().__call__(retry_state=retry_state)
|
||||
return random.uniform(0, high)
|
||||
|
||||
|
||||
class wait_exponential_jitter(wait_base):
|
||||
"""Wait strategy that applies exponential backoff and jitter.
|
||||
|
||||
It allows for a customized initial wait, maximum wait and jitter.
|
||||
|
||||
This implements the strategy described here:
|
||||
https://cloud.google.com/storage/docs/retry-strategy
|
||||
|
||||
The wait time is min(initial * 2**n + random.uniform(0, jitter), maximum)
|
||||
where n is the retry count.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
initial: float = 1,
|
||||
max: float = _utils.MAX_WAIT, # noqa
|
||||
exp_base: float = 2,
|
||||
jitter: float = 1,
|
||||
) -> None:
|
||||
self.initial = initial
|
||||
self.max = max
|
||||
self.exp_base = exp_base
|
||||
self.jitter = jitter
|
||||
|
||||
def __call__(self, retry_state: "RetryCallState") -> float:
|
||||
jitter = random.uniform(0, self.jitter)
|
||||
try:
|
||||
exp = self.exp_base ** (retry_state.attempt_number - 1)
|
||||
result = self.initial * exp + jitter
|
||||
except OverflowError:
|
||||
result = self.max
|
||||
return max(0, min(result, self.max))
|
||||
|
Reference in New Issue
Block a user