1
0
Fork 0
forked from bton/matekasse

tests versuch 2

This commit is contained in:
2000-Trek 2023-07-28 23:30:45 +02:00
parent fdf385fe06
commit c88f7df83a
2363 changed files with 408191 additions and 0 deletions

View file

@ -0,0 +1,608 @@
# Copyright 2016-2018 Julien Danjou
# Copyright 2017 Elisey Zanko
# Copyright 2016 Étienne Bersac
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools
import sys
import threading
import time
import typing as t
import warnings
from abc import ABC, abstractmethod
from concurrent import futures
from inspect import iscoroutinefunction
# Import all built-in retry strategies for easier usage.
from .retry import retry_base # noqa
from .retry import retry_all # noqa
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
from .retry import retry_never # noqa
from .retry import retry_unless_exception_type # noqa
from .retry import retry_if_exception_message # noqa
from .retry import retry_if_not_exception_message # noqa
# Import all nap strategies for easier usage.
from .nap import sleep # noqa
from .nap import sleep_using_event # noqa
# Import all built-in stop strategies for easier usage.
from .stop import stop_after_attempt # noqa
from .stop import stop_after_delay # noqa
from .stop import stop_all # noqa
from .stop import stop_any # noqa
from .stop import stop_never # noqa
from .stop import stop_when_event_set # noqa
# Import all built-in wait strategies for easier usage.
from .wait import wait_chain # noqa
from .wait import wait_combine # noqa
from .wait import wait_exponential # noqa
from .wait import wait_fixed # noqa
from .wait import wait_incrementing # noqa
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
from .before import before_nothing # noqa
# Import all built-in after strategies for easier usage.
from .after import after_log # noqa
from .after import after_nothing # noqa
# Import all built-in after strategies for easier usage.
from .before_sleep import before_sleep_log # noqa
from .before_sleep import before_sleep_nothing # noqa
# Replace a conditional import with a hard-coded None so that pip does
# not attempt to use tornado even if it is present in the environment.
# If tornado is non-None, tenacity will attempt to execute some code
# that is sensitive to the version of tornado, which could break pip
# if an old version is found.
tornado = None # type: ignore
if t.TYPE_CHECKING:
import types
from .retry import RetryBaseT
from .stop import StopBaseT
from .wait import WaitBaseT
WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any])
class TryAgain(Exception):
"""Always retry the executed function when raised."""
NO_RESULT = object()
class DoAttempt:
pass
class DoSleep(float):
pass
class BaseAction:
"""Base class for representing actions to take by retry object.
Concrete implementations must define:
- __init__: to initialize all necessary fields
- REPR_FIELDS: class variable specifying attributes to include in repr(self)
- NAME: for identification in retry object methods and callbacks
"""
REPR_FIELDS: t.Sequence[str] = ()
NAME: t.Optional[str] = None
def __repr__(self) -> str:
state_str = ", ".join(f"{field}={getattr(self, field)!r}" for field in self.REPR_FIELDS)
return f"{self.__class__.__name__}({state_str})"
def __str__(self) -> str:
return repr(self)
class RetryAction(BaseAction):
REPR_FIELDS = ("sleep",)
NAME = "retry"
def __init__(self, sleep: t.SupportsFloat) -> None:
self.sleep = float(sleep)
_unset = object()
def _first_set(first: t.Union[t.Any, object], second: t.Any) -> t.Any:
return second if first is _unset else first
class RetryError(Exception):
"""Encapsulates the last attempt instance right before giving up."""
def __init__(self, last_attempt: "Future") -> None:
self.last_attempt = last_attempt
super().__init__(last_attempt)
def reraise(self) -> "t.NoReturn":
if self.last_attempt.failed:
raise self.last_attempt.result()
raise self
def __str__(self) -> str:
return f"{self.__class__.__name__}[{self.last_attempt}]"
class AttemptManager:
"""Manage attempt context."""
def __init__(self, retry_state: "RetryCallState"):
self.retry_state = retry_state
def __enter__(self) -> None:
pass
def __exit__(
self,
exc_type: t.Optional[t.Type[BaseException]],
exc_value: t.Optional[BaseException],
traceback: t.Optional["types.TracebackType"],
) -> t.Optional[bool]:
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:
# We don't have the result, actually.
self.retry_state.set_result(None)
return None
class BaseRetrying(ABC):
def __init__(
self,
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,
):
self.sleep = sleep
self.stop = stop
self.wait = wait
self.retry = retry
self.before = before
self.after = after
self.before_sleep = before_sleep
self.reraise = reraise
self._local = threading.local()
self.retry_error_cls = retry_error_cls
self.retry_error_callback = retry_error_callback
def copy(
self,
sleep: t.Union[t.Callable[[t.Union[int, float]], None], 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,
before_sleep: t.Union[t.Optional[t.Callable[["RetryCallState"], None]], object] = _unset,
reraise: t.Union[bool, object] = _unset,
retry_error_cls: t.Union[t.Type[RetryError], object] = _unset,
retry_error_callback: t.Union[t.Optional[t.Callable[["RetryCallState"], t.Any]], object] = _unset,
) -> "BaseRetrying":
"""Copy this object with some parameters changed if needed."""
return self.__class__(
sleep=_first_set(sleep, self.sleep),
stop=_first_set(stop, self.stop),
wait=_first_set(wait, self.wait),
retry=_first_set(retry, self.retry),
before=_first_set(before, self.before),
after=_first_set(after, self.after),
before_sleep=_first_set(before_sleep, self.before_sleep),
reraise=_first_set(reraise, self.reraise),
retry_error_cls=_first_set(retry_error_cls, self.retry_error_cls),
retry_error_callback=_first_set(retry_error_callback, self.retry_error_callback),
)
def __repr__(self) -> str:
return (
f"<{self.__class__.__name__} object at 0x{id(self):x} ("
f"stop={self.stop}, "
f"wait={self.wait}, "
f"sleep={self.sleep}, "
f"retry={self.retry}, "
f"before={self.before}, "
f"after={self.after})>"
)
@property
def statistics(self) -> t.Dict[str, t.Any]:
"""Return a dictionary of runtime statistics.
This dictionary will be empty when the controller has never been
ran. When it is running or has ran previously it should have (but
may not) have useful and/or informational keys and values when
running is underway and/or completed.
.. warning:: The keys in this dictionary **should** be some what
stable (not changing), but there existence **may**
change between major releases as new statistics are
gathered or removed so before accessing keys ensure that
they actually exist and handle when they do not.
.. note:: The values in this dictionary are local to the thread
running call (so if multiple threads share the same retrying
object - either directly or indirectly) they will each have
there own view of statistics they have collected (in the
future we may provide a way to aggregate the various
statistics from each thread).
"""
try:
return self._local.statistics # type: ignore[no-any-return]
except AttributeError:
self._local.statistics = t.cast(t.Dict[str, t.Any], {})
return self._local.statistics
def wraps(self, f: WrappedFn) -> WrappedFn:
"""Wrap a function for retrying.
:param f: A function to wraps for retrying.
"""
@functools.wraps(f)
def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any:
return self(f, *args, **kw)
def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:
return self.copy(*args, **kwargs).wraps(f)
wrapped_f.retry = self # type: ignore[attr-defined]
wrapped_f.retry_with = retry_with # type: ignore[attr-defined]
return wrapped_f # type: ignore[return-value]
def begin(self) -> None:
self.statistics.clear()
self.statistics["start_time"] = time.monotonic()
self.statistics["attempt_number"] = 1
self.statistics["idle_for"] = 0
def iter(self, retry_state: "RetryCallState") -> t.Union[DoAttempt, DoSleep, t.Any]: # noqa
fut = retry_state.outcome
if fut is None:
if self.before is not None:
self.before(retry_state)
return DoAttempt()
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):
if self.retry_error_callback:
return self.retry_error_callback(retry_state)
retry_exc = self.retry_error_cls(fut)
if self.reraise:
raise retry_exc.reraise()
raise retry_exc from fut.exception()
if self.wait:
sleep = self.wait(retry_state)
else:
sleep = 0.0
retry_state.next_action = RetryAction(sleep)
retry_state.idle_for += sleep
self.statistics["idle_for"] += sleep
self.statistics["attempt_number"] += 1
if self.before_sleep is not None:
self.before_sleep(retry_state)
return DoSleep(sleep)
def __iter__(self) -> t.Generator[AttemptManager, None, None]:
self.begin()
retry_state = RetryCallState(self, fn=None, args=(), kwargs={})
while True:
do = self.iter(retry_state=retry_state)
if isinstance(do, DoAttempt):
yield AttemptManager(retry_state=retry_state)
elif isinstance(do, DoSleep):
retry_state.prepare_for_next_attempt()
self.sleep(do)
else:
break
@abstractmethod
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[..., WrappedFnReturnT],
*args: t.Any,
**kwargs: t.Any,
) -> WrappedFnReturnT:
self.begin()
retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
while True:
do = self.iter(retry_state=retry_state)
if isinstance(do, DoAttempt):
try:
result = fn(*args, **kwargs)
except BaseException: # noqa: B902
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 # type: ignore[no-any-return]
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:
super().__init__()
self.attempt_number = attempt_number
@property
def failed(self) -> bool:
"""Return whether a exception is being held in this future."""
return self.exception() is not None
@classmethod
def construct(cls, attempt_number: int, value: t.Any, has_exception: bool) -> "Future":
"""Construct a new Future object."""
fut = cls(attempt_number)
if has_exception:
fut.set_exception(value)
else:
fut.set_result(value)
return fut
class RetryCallState:
"""State related to a single call wrapped with Retrying."""
def __init__(
self,
retry_object: BaseRetrying,
fn: t.Optional[WrappedFn],
args: t.Any,
kwargs: t.Any,
) -> None:
#: Retry call start timestamp
self.start_time = time.monotonic()
#: Retry manager object
self.retry_object = retry_object
#: Function wrapped by this retry call
self.fn = fn
#: Arguments of the function wrapped by this retry call
self.args = args
#: Keyword arguments of the function wrapped by this retry call
self.kwargs = kwargs
#: The number of the current attempt
self.attempt_number: int = 1
#: Last outcome (result or exception) produced by the function
self.outcome: t.Optional[Future] = None
#: Timestamp of the last outcome
self.outcome_timestamp: t.Optional[float] = None
#: Time spent sleeping in retries
self.idle_for: float = 0.0
#: Next action as decided by the retry manager
self.next_action: t.Optional[RetryAction] = None
@property
def seconds_since_start(self) -> t.Optional[float]:
if self.outcome_timestamp is None:
return None
return self.outcome_timestamp - self.start_time
def prepare_for_next_attempt(self) -> None:
self.outcome = None
self.outcome_timestamp = None
self.attempt_number += 1
self.next_action = None
def set_result(self, val: t.Any) -> None:
ts = time.monotonic()
fut = Future(self.attempt_number)
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"]
) -> 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) -> str:
if self.outcome is None:
result = "none yet"
elif self.outcome.failed:
exception = self.outcome.exception()
result = f"failed ({exception.__class__.__name__} {exception})"
else:
result = f"returned {self.outcome.result()}"
slept = float(round(self.idle_for, 2))
clsname = self.__class__.__name__
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",
]

View file

@ -0,0 +1,94 @@
# Copyright 2016 Étienne Bersac
# Copyright 2016 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools
import sys
import typing as t
from asyncio import sleep
from pip._vendor.tenacity import AttemptManager
from pip._vendor.tenacity import BaseRetrying
from pip._vendor.tenacity import DoAttempt
from pip._vendor.tenacity import DoSleep
from pip._vendor.tenacity import RetryCallState
WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")
WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Awaitable[t.Any]])
class AsyncRetrying(BaseRetrying):
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[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)
while True:
do = self.iter(retry_state=retry_state)
if isinstance(do, DoAttempt):
try:
result = await fn(*args, **kwargs)
except BaseException: # noqa: B902
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 # 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) -> AttemptManager:
while True:
do = self.iter(retry_state=self._retry_state)
if do is None:
raise StopAsyncIteration
elif isinstance(do, DoAttempt):
return AttemptManager(retry_state=self._retry_state)
elif isinstance(do, DoSleep):
self._retry_state.prepare_for_next_attempt()
await self.sleep(do)
else:
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: t.Any, **kwargs: t.Any) -> t.Any:
return await fn(*args, **kwargs)
# Preserve attributes
async_wrapped.retry = fn.retry # type: ignore[attr-defined]
async_wrapped.retry_with = fn.retry_with # type: ignore[attr-defined]
return async_wrapped # type: ignore[return-value]

View file

@ -0,0 +1,76 @@
# Copyright 2016 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import typing
from datetime import timedelta
# sys.maxsize:
# An integer giving the maximum value a variable of type Py_ssize_t can take.
MAX_WAIT = sys.maxsize / 2
def find_ordinal(pos_num: int) -> str:
# See: https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers
if pos_num == 0:
return "th"
elif pos_num == 1:
return "st"
elif pos_num == 2:
return "nd"
elif pos_num == 3:
return "rd"
elif 4 <= pos_num <= 20:
return "th"
else:
return find_ordinal(pos_num % 10)
def to_ordinal(pos_num: int) -> str:
return f"{pos_num}{find_ordinal(pos_num)}"
def get_callback_name(cb: typing.Callable[..., typing.Any]) -> str:
"""Get a callback fully-qualified name.
If no name can be produced ``repr(cb)`` is called and returned.
"""
segments = []
try:
segments.append(cb.__qualname__)
except AttributeError:
try:
segments.append(cb.__name__)
except AttributeError:
pass
if not segments:
return repr(cb)
else:
try:
# When running under sphinx it appears this can be none?
if cb.__module__:
segments.insert(0, cb.__module__)
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)

View file

@ -0,0 +1,51 @@
# Copyright 2016 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import typing
from pip._vendor.tenacity import _utils
if typing.TYPE_CHECKING:
import logging
from pip._vendor.tenacity import RetryCallState
def after_nothing(retry_state: "RetryCallState") -> None:
"""After call strategy that does nothing."""
def after_log(
logger: "logging.Logger",
log_level: int,
sec_format: str = "%0.3f",
) -> typing.Callable[["RetryCallState"], None]:
"""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 '{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.",
)
return log_it

View file

@ -0,0 +1,46 @@
# Copyright 2016 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import typing
from pip._vendor.tenacity import _utils
if typing.TYPE_CHECKING:
import logging
from pip._vendor.tenacity import RetryCallState
def before_nothing(retry_state: "RetryCallState") -> None:
"""Before call strategy that does nothing."""
def before_log(logger: "logging.Logger", log_level: int) -> typing.Callable[["RetryCallState"], None]:
"""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 '{fn_name}', "
f"this is the {_utils.to_ordinal(retry_state.attempt_number)} time calling it.",
)
return log_it

View file

@ -0,0 +1,71 @@
# Copyright 2016 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import typing
from pip._vendor.tenacity import _utils
if typing.TYPE_CHECKING:
import logging
from pip._vendor.tenacity import RetryCallState
def before_sleep_nothing(retry_state: "RetryCallState") -> None:
"""Before call strategy that does nothing."""
def before_sleep_log(
logger: "logging.Logger",
log_level: int,
exc_info: bool = False,
) -> typing.Callable[["RetryCallState"], None]:
"""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}"
if exc_info:
local_exc_info = retry_state.outcome.exception()
else:
local_exc_info = False
else:
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 {fn_name} " f"in {retry_state.next_action.sleep} seconds as it {verb} {value}.",
exc_info=local_exc_info,
)
return log_it

View file

@ -0,0 +1,43 @@
# Copyright 2016 Étienne Bersac
# Copyright 2016 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import time
import typing
if typing.TYPE_CHECKING:
import threading
def sleep(seconds: float) -> None:
"""
Sleep strategy that delays execution for a given number of seconds.
This is the default strategy, and may be mocked out for unit testing.
"""
time.sleep(seconds)
class sleep_using_event:
"""Sleep strategy that waits on an event to be set."""
def __init__(self, event: "threading.Event") -> None:
self.event = event
def __call__(self, timeout: typing.Optional[float]) -> None:
# NOTE(harlowja): this may *not* actually wait for timeout
# seconds if the event is set (ie this may eject out early).
self.event.wait(timeout=timeout)

View file

@ -0,0 +1,272 @@
# Copyright 20162021 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import abc
import re
import typing
if typing.TYPE_CHECKING:
from pip._vendor.tenacity import RetryCallState
class retry_base(abc.ABC):
"""Abstract base class for retry strategies."""
@abc.abstractmethod
def __call__(self, retry_state: "RetryCallState") -> bool:
pass
def __and__(self, other: "retry_base") -> "retry_all":
return retry_all(self, other)
def __or__(self, other: "retry_base") -> "retry_any":
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."""
def __call__(self, retry_state: "RetryCallState") -> bool:
return False
retry_never = _retry_never()
class _retry_always(retry_base):
"""Retry strategy that always rejects any result."""
def __call__(self, retry_state: "RetryCallState") -> bool:
return True
retry_always = _retry_always()
class retry_if_exception(retry_base):
"""Retry strategy that retries if an exception verifies a predicate."""
def __init__(self, predicate: typing.Callable[[BaseException], bool]) -> None:
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:
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
class retry_if_exception_type(retry_if_exception):
"""Retries if an exception has been raised of one or more types."""
def __init__(
self,
exception_types: typing.Union[
typing.Type[BaseException],
typing.Tuple[typing.Type[BaseException], ...],
] = Exception,
) -> None:
self.exception_types = exception_types
super().__init__(lambda e: isinstance(e, exception_types))
class retry_if_not_exception_type(retry_if_exception):
"""Retries except an exception has been raised of one or more types."""
def __init__(
self,
exception_types: typing.Union[
typing.Type[BaseException],
typing.Tuple[typing.Type[BaseException], ...],
] = Exception,
) -> None:
self.exception_types = exception_types
super().__init__(lambda e: not isinstance(e, exception_types))
class retry_unless_exception_type(retry_if_exception):
"""Retries until an exception is raised of one or more types."""
def __init__(
self,
exception_types: typing.Union[
typing.Type[BaseException],
typing.Tuple[typing.Type[BaseException], ...],
] = Exception,
) -> None:
self.exception_types = exception_types
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
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):
"""Retries if the result verifies a predicate."""
def __init__(self, predicate: typing.Callable[[typing.Any], bool]) -> None:
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:
return False
class retry_if_not_result(retry_base):
"""Retries if the result refutes a predicate."""
def __init__(self, predicate: typing.Callable[[typing.Any], bool]) -> None:
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:
return False
class retry_if_exception_message(retry_if_exception):
"""Retries if an exception message equals or matches."""
def __init__(
self,
message: typing.Optional[str] = None,
match: typing.Optional[str] = None,
) -> None:
if message and match:
raise TypeError(f"{self.__class__.__name__}() takes either 'message' or 'match', not both")
# set predicate
if message:
def message_fnc(exception: BaseException) -> bool:
return message == str(exception)
predicate = message_fnc
elif match:
prog = re.compile(match)
def match_fnc(exception: BaseException) -> bool:
return bool(prog.match(str(exception)))
predicate = match_fnc
else:
raise TypeError(f"{self.__class__.__name__}() missing 1 required argument 'message' or 'match'")
super().__init__(predicate)
class retry_if_not_exception_message(retry_if_exception_message):
"""Retries until an exception message equals or matches."""
def __init__(
self,
message: typing.Optional[str] = None,
match: typing.Optional[str] = None,
) -> None:
super().__init__(message, match)
# invert predicate
if_predicate = self.predicate
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
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):
"""Retries if any of the retries condition is valid."""
def __init__(self, *retries: retry_base) -> None:
self.retries = retries
def __call__(self, retry_state: "RetryCallState") -> bool:
return any(r(retry_state) for r in self.retries)
class retry_all(retry_base):
"""Retries if all the retries condition are valid."""
def __init__(self, *retries: retry_base) -> None:
self.retries = retries
def __call__(self, retry_state: "RetryCallState") -> bool:
return all(r(retry_state) for r in self.retries)

View file

@ -0,0 +1,103 @@
# Copyright 20162021 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import abc
import typing
from pip._vendor.tenacity import _utils
if typing.TYPE_CHECKING:
import threading
from pip._vendor.tenacity import RetryCallState
class stop_base(abc.ABC):
"""Abstract base class for stop strategies."""
@abc.abstractmethod
def __call__(self, retry_state: "RetryCallState") -> bool:
pass
def __and__(self, other: "stop_base") -> "stop_all":
return stop_all(self, other)
def __or__(self, other: "stop_base") -> "stop_any":
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."""
def __init__(self, *stops: stop_base) -> None:
self.stops = stops
def __call__(self, retry_state: "RetryCallState") -> bool:
return any(x(retry_state) for x in self.stops)
class stop_all(stop_base):
"""Stop if all the stop conditions are valid."""
def __init__(self, *stops: stop_base) -> None:
self.stops = stops
def __call__(self, retry_state: "RetryCallState") -> bool:
return all(x(retry_state) for x in self.stops)
class _stop_never(stop_base):
"""Never stop."""
def __call__(self, retry_state: "RetryCallState") -> bool:
return False
stop_never = _stop_never()
class stop_when_event_set(stop_base):
"""Stop when the given event is set."""
def __init__(self, event: "threading.Event") -> None:
self.event = event
def __call__(self, retry_state: "RetryCallState") -> bool:
return self.event.is_set()
class stop_after_attempt(stop_base):
"""Stop when the previous attempt >= max_attempt."""
def __init__(self, max_attempt_number: int) -> None:
self.max_attempt_number = max_attempt_number
def __call__(self, retry_state: "RetryCallState") -> bool:
return retry_state.attempt_number >= self.max_attempt_number
class stop_after_delay(stop_base):
"""Stop when the time from the first attempt >= limit."""
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

View file

@ -0,0 +1,59 @@
# Copyright 2017 Elisey Zanko
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import typing
from pip._vendor.tenacity import BaseRetrying
from pip._vendor.tenacity import DoAttempt
from pip._vendor.tenacity import DoSleep
from pip._vendor.tenacity import RetryCallState
from tornado import gen
if typing.TYPE_CHECKING:
from tornado.concurrent import Future
_RetValT = typing.TypeVar("_RetValT")
class TornadoRetrying(BaseRetrying):
def __init__(self, sleep: "typing.Callable[[float], Future[None]]" = gen.sleep, **kwargs: typing.Any) -> None:
super().__init__(**kwargs)
self.sleep = sleep
@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,
**kwargs: typing.Any,
) -> "typing.Generator[typing.Any, typing.Any, _RetValT]":
self.begin()
retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)
while True:
do = self.iter(retry_state=retry_state)
if isinstance(do, DoAttempt):
try:
result = yield fn(*args, **kwargs)
except BaseException: # noqa: B902
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()
yield self.sleep(do)
else:
raise gen.Return(do)

View file

@ -0,0 +1,228 @@
# Copyright 20162021 Julien Danjou
# Copyright 2016 Joshua Harlow
# Copyright 2013-2014 Ray Holder
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import abc
import random
import typing
from pip._vendor.tenacity import _utils
if typing.TYPE_CHECKING:
from pip._vendor.tenacity import RetryCallState
class wait_base(abc.ABC):
"""Abstract base class for wait strategies."""
@abc.abstractmethod
def __call__(self, retry_state: "RetryCallState") -> float:
pass
def __add__(self, other: "wait_base") -> "wait_combine":
return wait_combine(self, other)
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: # 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: _utils.time_unit_type) -> None:
self.wait_fixed = _utils.to_seconds(wait)
def __call__(self, retry_state: "RetryCallState") -> float:
return self.wait_fixed
class wait_none(wait_fixed):
"""Wait strategy that doesn't wait at all before retrying."""
def __init__(self) -> None:
super().__init__(0)
class wait_random(wait_base):
"""Wait strategy that waits a random amount of time between min/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))
class wait_combine(wait_base):
"""Combine several waiting strategies."""
def __init__(self, *strategies: wait_base) -> None:
self.wait_funcs = strategies
def __call__(self, retry_state: "RetryCallState") -> float:
return sum(x(retry_state=retry_state) for x in self.wait_funcs)
class wait_chain(wait_base):
"""Chain two or more waiting strategies.
If all strategies are exhausted, the very last strategy is used
thereafter.
For example::
@retry(wait=wait_chain(*[wait_fixed(1) for i in range(3)] +
[wait_fixed(2) for j in range(5)] +
[wait_fixed(5) for k in range(4)))
def wait_chained():
print("Wait 1s for 3 attempts, 2s for 5 attempts and 5s
thereafter.")
"""
def __init__(self, *strategies: wait_base) -> None:
self.strategies = strategies
def __call__(self, retry_state: "RetryCallState") -> float:
wait_func_no = min(max(retry_state.attempt_number, 1), len(self.strategies))
wait_func = self.strategies[wait_func_no - 1]
return wait_func(retry_state=retry_state)
class wait_incrementing(wait_base):
"""Wait an incremental amount of time after each attempt.
Starting at a starting value and incrementing by a value for each attempt
(and restricting the upper limit to some maximum value).
"""
def __init__(
self,
start: _utils.time_unit_type = 0,
increment: _utils.time_unit_type = 100,
max: _utils.time_unit_type = _utils.MAX_WAIT, # noqa
) -> None:
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))
return max(0, min(result, self.max))
class wait_exponential(wait_base):
"""Wait strategy that applies exponential backoff.
It allows for a customized multiplier and an ability to restrict the
upper and lower limits to some maximum and minimum value.
The intervals are fixed (i.e. there is no jitter), so this strategy is
suitable for balancing retries against latency when a required resource is
unavailable for an unknown duration, but *not* suitable for resolving
contention between multiple processes for a shared resource. Use
wait_random_exponential for the latter case.
"""
def __init__(
self,
multiplier: typing.Union[int, float] = 1,
max: _utils.time_unit_type = _utils.MAX_WAIT, # noqa
exp_base: typing.Union[int, float] = 2,
min: _utils.time_unit_type = 0, # noqa
) -> None:
self.multiplier = multiplier
self.min = _utils.to_seconds(min)
self.max = _utils.to_seconds(max)
self.exp_base = exp_base
def __call__(self, retry_state: "RetryCallState") -> float:
try:
exp = self.exp_base ** (retry_state.attempt_number - 1)
result = self.multiplier * exp
except OverflowError:
return self.max
return max(max(0, self.min), min(result, self.max))
class wait_random_exponential(wait_exponential):
"""Random wait with exponentially widening window.
An exponential backoff strategy used to mediate contention between multiple
uncoordinated processes for a shared resource in distributed systems. This
is the sense in which "exponential backoff" is meant in e.g. Ethernet
networking, and corresponds to the "Full Jitter" algorithm described in
this blog post:
https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
Each retry occurs at a random time in a geometrically expanding interval.
It allows for a custom multiplier and an ability to restrict the upper
limit of the random interval to some maximum value.
Example::
wait_random_exponential(multiplier=0.5, # initial window 0.5s
max=60) # max 60s timeout
When waiting for an unavailable resource to become available again, as
opposed to trying to resolve contention for a shared resource, the
wait_exponential strategy (which uses a fixed interval) may be preferable.
"""
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))