Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
retry_base.py362 linesDownload Raw Back to retry
1# Copyright 2017 Google LLC2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7#     http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15"""Shared classes and functions for retrying requests.16 17:class:`_BaseRetry` is the base class for :class:`Retry`,18:class:`AsyncRetry`, :class:`StreamingRetry`, and :class:`AsyncStreamingRetry`.19"""20 21from __future__ import annotations22 23import logging24import random25import time26 27from enum import Enum28from typing import Any, Callable, Optional, TYPE_CHECKING29 30import requests.exceptions31 32from google.api_core import exceptions33from google.auth import exceptions as auth_exceptions34 35if TYPE_CHECKING:36    import sys37 38    if sys.version_info >= (3, 11):39        from typing import Self40    else:41        from typing_extensions import Self42 43_DEFAULT_INITIAL_DELAY = 1.0  # seconds44_DEFAULT_MAXIMUM_DELAY = 60.0  # seconds45_DEFAULT_DELAY_MULTIPLIER = 2.046_DEFAULT_DEADLINE = 60.0 * 2.0  # seconds47 48_LOGGER = logging.getLogger("google.api_core.retry")49 50 51def if_exception_type(52    *exception_types: type[Exception],53) -> Callable[[Exception], bool]:54    """Creates a predicate to check if the exception is of a given type.55 56    Args:57        exception_types (Sequence[:func:`type`]): The exception types to check58            for.59 60    Returns:61        Callable[Exception]: A predicate that returns True if the provided62            exception is of the given type(s).63    """64 65    def if_exception_type_predicate(exception: Exception) -> bool:66        """Bound predicate for checking an exception type."""67        return isinstance(exception, exception_types)68 69    return if_exception_type_predicate70 71 72# pylint: disable=invalid-name73# Pylint sees this as a constant, but it is also an alias that should be74# considered a function.75if_transient_error = if_exception_type(76    exceptions.InternalServerError,77    exceptions.TooManyRequests,78    exceptions.ServiceUnavailable,79    requests.exceptions.ConnectionError,80    requests.exceptions.ChunkedEncodingError,81    auth_exceptions.TransportError,82)83"""A predicate that checks if an exception is a transient API error.84 85The following server errors are considered transient:86 87- :class:`google.api_core.exceptions.InternalServerError` - HTTP 500, gRPC88    ``INTERNAL(13)`` and its subclasses.89- :class:`google.api_core.exceptions.TooManyRequests` - HTTP 42990- :class:`google.api_core.exceptions.ServiceUnavailable` - HTTP 50391- :class:`requests.exceptions.ConnectionError`92- :class:`requests.exceptions.ChunkedEncodingError` - The server declared93    chunked encoding but sent an invalid chunk.94- :class:`google.auth.exceptions.TransportError` - Used to indicate an95    error occurred during an HTTP request.96"""97# pylint: enable=invalid-name98 99 100def exponential_sleep_generator(101    initial: float, maximum: float, multiplier: float = _DEFAULT_DELAY_MULTIPLIER102):103    """Generates sleep intervals based on the exponential back-off algorithm.104 105    This implements the `Truncated Exponential Back-off`_ algorithm.106 107    .. _Truncated Exponential Back-off:108        https://cloud.google.com/storage/docs/exponential-backoff109 110    Args:111        initial (float): The minimum amount of time to delay. This must112            be greater than 0.113        maximum (float): The maximum amount of time to delay.114        multiplier (float): The multiplier applied to the delay.115 116    Yields:117        float: successive sleep intervals.118    """119    max_delay = min(initial, maximum)120    while True:121        yield random.uniform(0.0, max_delay)122        max_delay = min(max_delay * multiplier, maximum)123 124 125class RetryFailureReason(Enum):126    """127    The cause of a failed retry, used when building exceptions128    """129 130    TIMEOUT = 0131    NON_RETRYABLE_ERROR = 1132 133 134def build_retry_error(135    exc_list: list[Exception],136    reason: RetryFailureReason,137    timeout_val: float | None,138    **kwargs: Any,139) -> tuple[Exception, Exception | None]:140    """141    Default exception_factory implementation.142 143    Returns a RetryError if the failure is due to a timeout, otherwise144    returns the last exception encountered.145 146    Args:147      - exc_list: list of exceptions that occurred during the retry148      - reason: reason for the retry failure.149            Can be TIMEOUT or NON_RETRYABLE_ERROR150      - timeout_val: the original timeout value for the retry (in seconds), for use in the exception message151 152    Returns:153      - tuple: a tuple of the exception to be raised, and the cause exception if any154    """155    if reason == RetryFailureReason.TIMEOUT:156        # return RetryError with the most recent exception as the cause157        src_exc = exc_list[-1] if exc_list else None158        timeout_val_str = f"of {timeout_val:0.1f}s " if timeout_val is not None else ""159        return (160            exceptions.RetryError(161                f"Timeout {timeout_val_str}exceeded",162                src_exc,163            ),164            src_exc,165        )166    elif exc_list:167        # return most recent exception encountered168        return exc_list[-1], None169    else:170        # no exceptions were given in exc_list. Raise generic RetryError171        return exceptions.RetryError("Unknown error", None), None172 173 174def _retry_error_helper(175    exc: Exception,176    deadline: float | None,177    next_sleep: float,178    error_list: list[Exception],179    predicate_fn: Callable[[Exception], bool],180    on_error_fn: Callable[[Exception], None] | None,181    exc_factory_fn: Callable[182        [list[Exception], RetryFailureReason, float | None],183        tuple[Exception, Exception | None],184    ],185    original_timeout: float | None,186):187    """188    Shared logic for handling an error for all retry implementations189 190    - Raises an error on timeout or non-retryable error191    - Calls on_error_fn if provided192    - Logs the error193 194    Args:195       - exc: the exception that was raised196       - deadline: the deadline for the retry, calculated as a diff from time.monotonic()197       - next_sleep: the next sleep interval198       - error_list: the list of exceptions that have been raised so far199       - predicate_fn: takes `exc` and returns true if the operation should be retried200       - on_error_fn: callback to execute when a retryable error occurs201       - exc_factory_fn: callback used to build the exception to be raised on terminal failure202       - original_timeout_val: the original timeout value for the retry (in seconds),203           to be passed to the exception factory for building an error message204    """205    error_list.append(exc)206    if not predicate_fn(exc):207        final_exc, source_exc = exc_factory_fn(208            error_list,209            RetryFailureReason.NON_RETRYABLE_ERROR,210            original_timeout,211        )212        raise final_exc from source_exc213    if on_error_fn is not None:214        on_error_fn(exc)215    if deadline is not None and time.monotonic() + next_sleep > deadline:216        final_exc, source_exc = exc_factory_fn(217            error_list,218            RetryFailureReason.TIMEOUT,219            original_timeout,220        )221        raise final_exc from source_exc222    _LOGGER.debug(223        "Retrying due to {}, sleeping {:.1f}s ...".format(error_list[-1], next_sleep)224    )225 226 227class _BaseRetry(object):228    """229    Base class for retry configuration objects. This class is intended to capture retry230    and backoff configuration that is common to both synchronous and asynchronous retries,231    for both unary and streaming RPCs. It is not intended to be instantiated directly,232    but rather to be subclassed by the various retry configuration classes.233    """234 235    def __init__(236        self,237        predicate: Callable[[Exception], bool] = if_transient_error,238        initial: float = _DEFAULT_INITIAL_DELAY,239        maximum: float = _DEFAULT_MAXIMUM_DELAY,240        multiplier: float = _DEFAULT_DELAY_MULTIPLIER,241        timeout: Optional[float] = _DEFAULT_DEADLINE,242        on_error: Optional[Callable[[Exception], Any]] = None,243        **kwargs: Any,244    ) -> None:245        self._predicate = predicate246        self._initial = initial247        self._multiplier = multiplier248        self._maximum = maximum249        self._timeout = kwargs.get("deadline", timeout)250        self._deadline = self._timeout251        self._on_error = on_error252 253    def __call__(self, *args, **kwargs) -> Any:254        raise NotImplementedError("Not implemented in base class")255 256    @property257    def deadline(self) -> float | None:258        """259        DEPRECATED: use ``timeout`` instead.  Refer to the ``Retry`` class260        documentation for details.261        """262        return self._timeout263 264    @property265    def timeout(self) -> float | None:266        return self._timeout267 268    def with_deadline(self, deadline: float | None) -> Self:269        """Return a copy of this retry with the given timeout.270 271        DEPRECATED: use :meth:`with_timeout` instead. Refer to the ``Retry`` class272        documentation for details.273 274        Args:275            deadline (float|None): How long to keep retrying, in seconds. If None,276                no timeout is enforced.277 278        Returns:279            Retry: A new retry instance with the given timeout.280        """281        return self.with_timeout(deadline)282 283    def with_timeout(self, timeout: float | None) -> Self:284        """Return a copy of this retry with the given timeout.285 286        Args:287            timeout (float): How long to keep retrying, in seconds. If None,288                no timeout will be enforced.289 290        Returns:291            Retry: A new retry instance with the given timeout.292        """293        return type(self)(294            predicate=self._predicate,295            initial=self._initial,296            maximum=self._maximum,297            multiplier=self._multiplier,298            timeout=timeout,299            on_error=self._on_error,300        )301 302    def with_predicate(self, predicate: Callable[[Exception], bool]) -> Self:303        """Return a copy of this retry with the given predicate.304 305        Args:306            predicate (Callable[Exception]): A callable that should return307                ``True`` if the given exception is retryable.308 309        Returns:310            Retry: A new retry instance with the given predicate.311        """312        return type(self)(313            predicate=predicate,314            initial=self._initial,315            maximum=self._maximum,316            multiplier=self._multiplier,317            timeout=self._timeout,318            on_error=self._on_error,319        )320 321    def with_delay(322        self,323        initial: Optional[float] = None,324        maximum: Optional[float] = None,325        multiplier: Optional[float] = None,326    ) -> Self:327        """Return a copy of this retry with the given delay options.328 329        Args:330            initial (float): The minimum amount of time to delay (in seconds). This must331                be greater than 0. If None, the current value is used.332            maximum (float): The maximum amount of time to delay (in seconds). If None, the333                current value is used.334            multiplier (float): The multiplier applied to the delay. If None, the current335                value is used.336 337        Returns:338            Retry: A new retry instance with the given delay options.339        """340        return type(self)(341            predicate=self._predicate,342            initial=initial if initial is not None else self._initial,343            maximum=maximum if maximum is not None else self._maximum,344            multiplier=multiplier if multiplier is not None else self._multiplier,345            timeout=self._timeout,346            on_error=self._on_error,347        )348 349    def __str__(self) -> str:350        return (351            "<{} predicate={}, initial={:.1f}, maximum={:.1f}, "352            "multiplier={:.1f}, timeout={}, on_error={}>".format(353                type(self).__name__,354                self._predicate,355                self._initial,356                self._maximum,357                self._multiplier,358                self._timeout,  # timeout can be None, thus no {:.1f}359                self._on_error,360            )361        )362 
codekingpro/portable-devtools · Team Ai