diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-07-02 11:20:50 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-07-02 11:31:57 +0300 |
commit | 5f2323ef7bd3c3874a5dbbcaf67db0cc094117ab (patch) | |
tree | 75b5e2730de828fb35d22cbf1a6c504606825cd6 /contrib/python | |
parent | e6141b6d4e256d98ce597ab2491e70bf90e7338a (diff) | |
download | ydb-5f2323ef7bd3c3874a5dbbcaf67db0cc094117ab.tar.gz |
Intermediate changes
Diffstat (limited to 'contrib/python')
-rw-r--r-- | contrib/python/tenacity/py3/.dist-info/METADATA | 2 | ||||
-rw-r--r-- | contrib/python/tenacity/py3/README.rst | 20 | ||||
-rw-r--r-- | contrib/python/tenacity/py3/tenacity/__init__.py | 28 | ||||
-rw-r--r-- | contrib/python/tenacity/py3/tenacity/_utils.py | 12 | ||||
-rw-r--r-- | contrib/python/tenacity/py3/tenacity/asyncio/__init__.py (renamed from contrib/python/tenacity/py3/tenacity/_asyncio.py) | 101 | ||||
-rw-r--r-- | contrib/python/tenacity/py3/tenacity/asyncio/retry.py | 125 | ||||
-rw-r--r-- | contrib/python/tenacity/py3/tenacity/retry.py | 10 | ||||
-rw-r--r-- | contrib/python/tenacity/py3/ya.make | 5 |
8 files changed, 258 insertions, 45 deletions
diff --git a/contrib/python/tenacity/py3/.dist-info/METADATA b/contrib/python/tenacity/py3/.dist-info/METADATA index 3e509464e2..cd789a8975 100644 --- a/contrib/python/tenacity/py3/.dist-info/METADATA +++ b/contrib/python/tenacity/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: tenacity -Version: 8.3.0 +Version: 8.4.1 Summary: Retry code until it succeeds Home-page: https://github.com/jd/tenacity Author: Julien Danjou diff --git a/contrib/python/tenacity/py3/README.rst b/contrib/python/tenacity/py3/README.rst index bdf7ff2117..65dd208bdf 100644 --- a/contrib/python/tenacity/py3/README.rst +++ b/contrib/python/tenacity/py3/README.rst @@ -79,7 +79,7 @@ Examples Basic Retry ~~~~~~~~~~~ -.. testsetup:: * +.. testsetup:: import logging # @@ -568,28 +568,34 @@ in retry strategies like ``retry_if_result``. This can be done accessing the Async and retry ~~~~~~~~~~~~~~~ -Finally, ``retry`` works also on asyncio and Tornado (>= 4.5) coroutines. +Finally, ``retry`` works also on asyncio, Trio, and Tornado (>= 4.5) coroutines. Sleeps are done asynchronously too. .. code-block:: python @retry - async def my_async_function(loop): + async def my_asyncio_function(loop): await loop.getaddrinfo('8.8.8.8', 53) .. code-block:: python @retry + async def my_async_trio_function(): + await trio.socket.getaddrinfo('8.8.8.8', 53) + +.. code-block:: python + + @retry @tornado.gen.coroutine - def my_async_function(http_client, url): + def my_async_tornado_function(http_client, url): yield http_client.fetch(url) -You can even use alternative event loops such as `curio` or `Trio` by passing the correct sleep function: +You can even use alternative event loops such as `curio` by passing the correct sleep function: .. code-block:: python - @retry(sleep=trio.sleep) - async def my_async_function(loop): + @retry(sleep=curio.sleep) + async def my_async_curio_function(): await asks.get('https://example.org') Contribute diff --git a/contrib/python/tenacity/py3/tenacity/__init__.py b/contrib/python/tenacity/py3/tenacity/__init__.py index bcee3f5910..7de36d4345 100644 --- a/contrib/python/tenacity/py3/tenacity/__init__.py +++ b/contrib/python/tenacity/py3/tenacity/__init__.py @@ -24,7 +24,8 @@ import typing as t import warnings from abc import ABC, abstractmethod from concurrent import futures -from inspect import iscoroutinefunction + +from . import _utils # Import all built-in retry strategies for easier usage. from .retry import retry_base # noqa @@ -87,6 +88,7 @@ except ImportError: if t.TYPE_CHECKING: import types + from . import asyncio as tasyncio from .retry import RetryBaseT from .stop import StopBaseT from .wait import WaitBaseT @@ -593,16 +595,24 @@ def retry(func: WrappedFn) -> WrappedFn: ... @t.overload def retry( - sleep: t.Callable[[t.Union[int, float]], t.Optional[t.Awaitable[None]]] = sleep, + sleep: t.Callable[[t.Union[int, float]], t.Union[None, t.Awaitable[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, + retry: "t.Union[RetryBaseT, tasyncio.retry.RetryBaseT]" = retry_if_exception_type(), + before: t.Callable[ + ["RetryCallState"], t.Union[None, t.Awaitable[None]] + ] = before_nothing, + after: t.Callable[ + ["RetryCallState"], t.Union[None, t.Awaitable[None]] + ] = after_nothing, + before_sleep: t.Optional[ + t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]] + ] = None, reraise: bool = False, retry_error_cls: t.Type["RetryError"] = RetryError, - retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None, + retry_error_callback: t.Optional[ + t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]] + ] = None, ) -> t.Callable[[WrappedFn], WrappedFn]: ... @@ -624,7 +634,7 @@ def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any: f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)" ) r: "BaseRetrying" - if iscoroutinefunction(f): + if _utils.is_coroutine_callable(f): r = AsyncRetrying(*dargs, **dkw) elif ( tornado @@ -640,7 +650,7 @@ def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any: return wrap -from tenacity._asyncio import AsyncRetrying # noqa:E402,I100 +from tenacity.asyncio import AsyncRetrying # noqa:E402,I100 if tornado: from tenacity.tornadoweb import TornadoRetrying diff --git a/contrib/python/tenacity/py3/tenacity/_utils.py b/contrib/python/tenacity/py3/tenacity/_utils.py index 4e34115e0e..f11a088814 100644 --- a/contrib/python/tenacity/py3/tenacity/_utils.py +++ b/contrib/python/tenacity/py3/tenacity/_utils.py @@ -87,3 +87,15 @@ def is_coroutine_callable(call: typing.Callable[..., typing.Any]) -> bool: partial_call = isinstance(call, functools.partial) and call.func dunder_call = partial_call or getattr(call, "__call__", None) return inspect.iscoroutinefunction(dunder_call) + + +def wrap_to_async_func( + call: typing.Callable[..., typing.Any], +) -> typing.Callable[..., typing.Awaitable[typing.Any]]: + if is_coroutine_callable(call): + return call + + async def inner(*args: typing.Any, **kwargs: typing.Any) -> typing.Any: + return call(*args, **kwargs) + + return inner diff --git a/contrib/python/tenacity/py3/tenacity/_asyncio.py b/contrib/python/tenacity/py3/tenacity/asyncio/__init__.py index b06303f484..6d63ebcfab 100644 --- a/contrib/python/tenacity/py3/tenacity/_asyncio.py +++ b/contrib/python/tenacity/py3/tenacity/asyncio/__init__.py @@ -19,34 +19,87 @@ import functools import sys import typing as t +import tenacity from tenacity import AttemptManager from tenacity import BaseRetrying from tenacity import DoAttempt from tenacity import DoSleep from tenacity import RetryCallState +from tenacity import RetryError +from tenacity import after_nothing +from tenacity import before_nothing from tenacity import _utils +# Import all built-in retry strategies for easier usage. +from .retry import RetryBaseT +from .retry import retry_all # noqa +from .retry import retry_any # noqa +from .retry import retry_if_exception # noqa +from .retry import retry_if_result # noqa +from ..retry import RetryBaseT as SyncRetryBaseT + +if t.TYPE_CHECKING: + from tenacity.stop import StopBaseT + from tenacity.wait import WaitBaseT + WrappedFnReturnT = t.TypeVar("WrappedFnReturnT") WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Awaitable[t.Any]]) -def asyncio_sleep(duration: float) -> t.Awaitable[None]: +def _portable_async_sleep(seconds: float) -> t.Awaitable[None]: + # If trio is already imported, then importing it is cheap. + # If trio isn't already imported, then it's definitely not running, so we + # can skip further checks. + if "trio" in sys.modules: + # If trio is available, then sniffio is too + import trio + import sniffio + + if sniffio.current_async_library() == "trio": + return trio.sleep(seconds) + # Otherwise, assume asyncio # Lazy import asyncio as it's expensive (responsible for 25-50% of total import overhead). import asyncio - return asyncio.sleep(duration) + return asyncio.sleep(seconds) class AsyncRetrying(BaseRetrying): - sleep: t.Callable[[float], t.Awaitable[t.Any]] - def __init__( self, - sleep: t.Callable[[float], t.Awaitable[t.Any]] = asyncio_sleep, - **kwargs: t.Any, + sleep: t.Callable[ + [t.Union[int, float]], t.Union[None, t.Awaitable[None]] + ] = _portable_async_sleep, + stop: "StopBaseT" = tenacity.stop.stop_never, + wait: "WaitBaseT" = tenacity.wait.wait_none(), + retry: "t.Union[SyncRetryBaseT, RetryBaseT]" = tenacity.retry_if_exception_type(), + before: t.Callable[ + ["RetryCallState"], t.Union[None, t.Awaitable[None]] + ] = before_nothing, + after: t.Callable[ + ["RetryCallState"], t.Union[None, t.Awaitable[None]] + ] = after_nothing, + before_sleep: t.Optional[ + t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]] + ] = None, + reraise: bool = False, + retry_error_cls: t.Type["RetryError"] = RetryError, + retry_error_callback: t.Optional[ + t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]] + ] = None, ) -> None: - super().__init__(**kwargs) - self.sleep = sleep + super().__init__( + sleep=sleep, # type: ignore[arg-type] + stop=stop, + wait=wait, + retry=retry, # type: ignore[arg-type] + before=before, # type: ignore[arg-type] + after=after, # type: ignore[arg-type] + before_sleep=before_sleep, # type: ignore[arg-type] + reraise=reraise, + retry_error_cls=retry_error_cls, + retry_error_callback=retry_error_callback, + ) async def __call__( # type: ignore[override] self, fn: WrappedFn, *args: t.Any, **kwargs: t.Any @@ -65,31 +118,21 @@ class AsyncRetrying(BaseRetrying): retry_state.set_result(result) elif isinstance(do, DoSleep): retry_state.prepare_for_next_attempt() - await self.sleep(do) + await self.sleep(do) # type: ignore[misc] else: return do # type: ignore[no-any-return] - @classmethod - def _wrap_action_func(cls, fn: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: - if _utils.is_coroutine_callable(fn): - return fn - - async def inner(*args: t.Any, **kwargs: t.Any) -> t.Any: - return fn(*args, **kwargs) - - return inner - def _add_action_func(self, fn: t.Callable[..., t.Any]) -> None: - self.iter_state.actions.append(self._wrap_action_func(fn)) + self.iter_state.actions.append(_utils.wrap_to_async_func(fn)) async def _run_retry(self, retry_state: "RetryCallState") -> None: # type: ignore[override] - self.iter_state.retry_run_result = await self._wrap_action_func(self.retry)( + self.iter_state.retry_run_result = await _utils.wrap_to_async_func(self.retry)( retry_state ) async def _run_wait(self, retry_state: "RetryCallState") -> None: # type: ignore[override] if self.wait: - sleep = await self._wrap_action_func(self.wait)(retry_state) + sleep = await _utils.wrap_to_async_func(self.wait)(retry_state) else: sleep = 0.0 @@ -97,7 +140,7 @@ class AsyncRetrying(BaseRetrying): async def _run_stop(self, retry_state: "RetryCallState") -> None: # type: ignore[override] self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start - self.iter_state.stop_run_result = await self._wrap_action_func(self.stop)( + self.iter_state.stop_run_result = await _utils.wrap_to_async_func(self.stop)( retry_state ) @@ -127,7 +170,7 @@ class AsyncRetrying(BaseRetrying): return AttemptManager(retry_state=self._retry_state) elif isinstance(do, DoSleep): self._retry_state.prepare_for_next_attempt() - await self.sleep(do) + await self.sleep(do) # type: ignore[misc] else: raise StopAsyncIteration @@ -146,3 +189,13 @@ class AsyncRetrying(BaseRetrying): async_wrapped.retry_with = fn.retry_with # type: ignore[attr-defined] return async_wrapped # type: ignore[return-value] + + +__all__ = [ + "retry_all", + "retry_any", + "retry_if_exception", + "retry_if_result", + "WrappedFn", + "AsyncRetrying", +] diff --git a/contrib/python/tenacity/py3/tenacity/asyncio/retry.py b/contrib/python/tenacity/py3/tenacity/asyncio/retry.py new file mode 100644 index 0000000000..94b8b15428 --- /dev/null +++ b/contrib/python/tenacity/py3/tenacity/asyncio/retry.py @@ -0,0 +1,125 @@ +# Copyright 2016–2021 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 tenacity import _utils +from tenacity import retry_base + +if typing.TYPE_CHECKING: + from tenacity import RetryCallState + + +class async_retry_base(retry_base): + """Abstract base class for async retry strategies.""" + + @abc.abstractmethod + async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override] + pass + + def __and__( # type: ignore[override] + self, other: "typing.Union[retry_base, async_retry_base]" + ) -> "retry_all": + return retry_all(self, other) + + def __rand__( # type: ignore[misc,override] + self, other: "typing.Union[retry_base, async_retry_base]" + ) -> "retry_all": + return retry_all(other, self) + + def __or__( # type: ignore[override] + self, other: "typing.Union[retry_base, async_retry_base]" + ) -> "retry_any": + return retry_any(self, other) + + def __ror__( # type: ignore[misc,override] + self, other: "typing.Union[retry_base, async_retry_base]" + ) -> "retry_any": + return retry_any(other, self) + + +RetryBaseT = typing.Union[ + async_retry_base, typing.Callable[["RetryCallState"], typing.Awaitable[bool]] +] + + +class retry_if_exception(async_retry_base): + """Retry strategy that retries if an exception verifies a predicate.""" + + def __init__( + self, predicate: typing.Callable[[BaseException], typing.Awaitable[bool]] + ) -> None: + self.predicate = predicate + + async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override] + 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 await self.predicate(exception) + else: + return False + + +class retry_if_result(async_retry_base): + """Retries if the result verifies a predicate.""" + + def __init__( + self, predicate: typing.Callable[[typing.Any], typing.Awaitable[bool]] + ) -> None: + self.predicate = predicate + + async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override] + if retry_state.outcome is None: + raise RuntimeError("__call__() called before outcome was set") + + if not retry_state.outcome.failed: + return await self.predicate(retry_state.outcome.result()) + else: + return False + + +class retry_any(async_retry_base): + """Retries if any of the retries condition is valid.""" + + def __init__(self, *retries: typing.Union[retry_base, async_retry_base]) -> None: + self.retries = retries + + async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override] + result = False + for r in self.retries: + result = result or await _utils.wrap_to_async_func(r)(retry_state) + if result: + break + return result + + +class retry_all(async_retry_base): + """Retries if all the retries condition are valid.""" + + def __init__(self, *retries: typing.Union[retry_base, async_retry_base]) -> None: + self.retries = retries + + async def __call__(self, retry_state: "RetryCallState") -> bool: # type: ignore[override] + result = True + for r in self.retries: + result = result and await _utils.wrap_to_async_func(r)(retry_state) + if not result: + break + return result diff --git a/contrib/python/tenacity/py3/tenacity/retry.py b/contrib/python/tenacity/py3/tenacity/retry.py index c5e55a653f..9211631bd8 100644 --- a/contrib/python/tenacity/py3/tenacity/retry.py +++ b/contrib/python/tenacity/py3/tenacity/retry.py @@ -30,10 +30,16 @@ class retry_base(abc.ABC): pass def __and__(self, other: "retry_base") -> "retry_all": - return retry_all(self, other) + return other.__rand__(self) + + def __rand__(self, other: "retry_base") -> "retry_all": + return retry_all(other, self) def __or__(self, other: "retry_base") -> "retry_any": - return retry_any(self, other) + return other.__ror__(self) + + def __ror__(self, other: "retry_base") -> "retry_any": + return retry_any(other, self) RetryBaseT = typing.Union[retry_base, typing.Callable[["RetryCallState"], bool]] diff --git a/contrib/python/tenacity/py3/ya.make b/contrib/python/tenacity/py3/ya.make index 1670ca9908..d75e15b99f 100644 --- a/contrib/python/tenacity/py3/ya.make +++ b/contrib/python/tenacity/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(8.3.0) +VERSION(8.4.1) LICENSE(Apache-2.0) @@ -15,9 +15,10 @@ NO_CHECK_IMPORTS( PY_SRCS( TOP_LEVEL tenacity/__init__.py - tenacity/_asyncio.py tenacity/_utils.py tenacity/after.py + tenacity/asyncio/__init__.py + tenacity/asyncio/retry.py tenacity/before.py tenacity/before_sleep.py tenacity/nap.py |