def __init__( self, when=None, max_attempts=None, on_exception=None, stop=None, backoff=None, ): if stop is None: if max_attempts is not None: stop = stop_mod.after_attempt(max_attempts) else: stop = stop_mod.NEVER if on_exception is not None: when = when_mod.raises(on_exception) | when if when is None: when = self._DEFAULT_PREDICATE if backoff is None: backoff = backoff_mod.jittered() if not isinstance(backoff, backoff_mod.RetryBackoff): backoff = backoff_mod.from_iterable_factory(backoff) self._when = when self._backoff = backoff self._stop = stop
def test_when_or_operator_with_exception(mocker, request_builder): # Setup response = mocker.Mock() response.status_code = 401 predicate = when.status(401) | when.raises(retry.BASE_CLIENT_EXCEPTION) # Verify: Calls __call__ for both predicates request_builder.client.exceptions.BaseClientException = RuntimeError predicate = predicate(request_builder) # Verify: when left predicate matches assert predicate.should_retry_after_response(response) is True # Verify: when right predicate matches assert (predicate.should_retry_after_exception( RuntimeError, RuntimeError(), None) is True) # Verify: when neither matches assert (predicate.should_retry_after_exception(Exception, Exception(), None) is False)
def __init__( self, when=None, max_attempts=None, on_exception=None, stop=None, backoff=None, ): if stop is not None: self._stop = stop elif max_attempts is not None: self._stop = stop_mod.after_attempt(max_attempts) else: self._stop = stop_mod.DISABLE self._predicate = when if on_exception is not None: self._predicate = when_mod.raises(on_exception) | self._predicate if self._predicate is None: self._predicate = self._DEFAULT_PREDICATE self._backoff = backoff_mod.jittered() if backoff is None else backoff
from anacreonlib.types.request_datatypes import * from anacreonlib.types.response_datatypes import ( AuthenticationResponse, convert_json_to_anacreon_obj, AnacreonObject, handle_hexarc_error_response, ) from anacreonlib.types.scenario_info_datatypes import ( ScenarioInfo, convert_json_to_scenario_info, ) @uplink.timeout(10) @uplink.retry(when=raises(retry.CONNECTION_TIMEOUT)) @uplink.json @returns.json @handle_hexarc_error_response class AnacreonAsyncClient(Consumer): """ A coroutine-based asynchronous API client to interact with anacreon """ def __init__( self, *, base_url: str = "https://anacreon.kronosaur.com/api/") -> None: self._aio_session = aiohttp.ClientSession() super().__init__( base_url=base_url, client=clients.AiohttpClient(session=self._aio_session),
class retry(decorators.MethodAnnotation): """ A decorator that adds retry support to a consumer method or to an entire consumer. Unless you specify the ``when`` or ``on_exception`` argument, all failed requests that raise an exception are retried. Unless you specify the ``max_attempts`` or ``stop`` argument, this decorator continues retrying until the server returns a response. Unless you specify the ``backoff`` argument, this decorator uses `capped exponential backoff and jitter <https://amzn.to/2xc2nK2>`_, which should benefit performance with remote services under high contention. Args: when (optional): A predicate that determines when a retry should be attempted. max_attempts (int, optional): The number of retries to attempt. If not specified, requests are retried continuously until a response is rendered. on_exception (:class:`Exception`, optional): The exception type that should prompt a retry attempt. stop (:obj:`callable`, optional): A function that creates predicates that decide when to stop retrying a request. backoff (:obj:`callable`, optional): A function that creates an iterator over the ordered sequence of timeouts between retries. If not specified, exponential backoff is used. """ _DEFAULT_PREDICATE = when_mod.raises(Exception) def __init__( self, when=None, max_attempts=None, on_exception=None, stop=None, backoff=None, ): if stop is not None: self._stop = stop elif max_attempts is not None: self._stop = stop_mod.after_attempt(max_attempts) else: self._stop = stop_mod.DISABLE self._predicate = when if on_exception is not None: self._predicate = when_mod.raises(on_exception) | self._predicate if self._predicate is None: self._predicate = self._DEFAULT_PREDICATE self._backoff = backoff_mod.jittered() if backoff is None else backoff BASE_CLIENT_EXCEPTION = ClientExceptionProxy( lambda ex: ex.BaseClientException) CONNECTION_ERROR = ClientExceptionProxy(lambda ex: ex.ConnectionError) CONNECTION_TIMEOUT = ClientExceptionProxy(lambda ex: ex.ConnectionTimeout) SERVER_TIMEOUT = ClientExceptionProxy(lambda ex: ex.ServerTimeout) SSL_ERROR = ClientExceptionProxy(lambda ex: ex.SSLError) def modify_request(self, request_builder): request_builder.add_request_template( self._create_template(request_builder)) def _create_template(self, request_builder): return RetryTemplate(self._backoff_iterator(), self._predicate(request_builder)) def _backoff_iterator(self): stop_gen = self._stop() for delay in self._backoff(): next(stop_gen) if stop_gen.send(delay): break yield delay stop = stop_mod backoff = backoff_mod when = when_mod
class retry(decorators.MethodAnnotation): """ A decorator that adds retry support to a consumer method or to an entire consumer. Unless you specify the ``when`` or ``on_exception`` argument, all failed requests that raise an exception are retried. Unless you specify the ``max_attempts`` or ``stop`` argument, this decorator continues retrying until the server returns a response. Unless you specify the ``backoff`` argument, this decorator uses `capped exponential backoff and jitter <https://amzn.to/2xc2nK2>`_, which should benefit performance with remote services under high contention. .. note:: Response and error handlers (see :ref:`here <custom response handler>`) are invoked after the retry condition breaks or all retry attempts are exhausted, whatever comes first. These handlers will receive the first response/exception that triggers the retry's ``stop`` condition or doesn't match its ``when`` filter. In other words, responses or exceptions that match the retry condition (e.g., retry when status code is 5xx) are not subject to response or error handlers as long as the request doesn't break the retry's stop condition (e.g., stop retrying after 5 attempts). Args: when (optional): A predicate that determines when a retry should be attempted. max_attempts (int, optional): The number of retries to attempt. If not specified, requests are retried continuously until a response is rendered. on_exception (:class:`Exception`, optional): The exception type that should prompt a retry attempt. stop (:obj:`callable`, optional): A function that creates predicates that decide when to stop retrying a request. backoff (:class:`RetryBackoff`, :obj:`callable`, optional): A backoff strategy or a function that creates an iterator over the ordered sequence of timeouts between retries. If not specified, exponential backoff is used. """ _DEFAULT_PREDICATE = when_mod.raises(Exception) stop = stop_mod backoff = backoff_mod when = when_mod def __init__( self, when=None, max_attempts=None, on_exception=None, stop=None, backoff=None, ): if stop is None: if max_attempts is not None: stop = stop_mod.after_attempt(max_attempts) else: stop = stop_mod.NEVER if on_exception is not None: when = when_mod.raises(on_exception) | when if when is None: when = self._DEFAULT_PREDICATE if backoff is None: backoff = backoff_mod.jittered() if not isinstance(backoff, backoff_mod.RetryBackoff): backoff = backoff_mod.from_iterable_factory(backoff) self._when = when self._backoff = backoff self._stop = stop BASE_CLIENT_EXCEPTION = ClientExceptionProxy( lambda ex: ex.BaseClientException ) CONNECTION_ERROR = ClientExceptionProxy(lambda ex: ex.ConnectionError) CONNECTION_TIMEOUT = ClientExceptionProxy(lambda ex: ex.ConnectionTimeout) SERVER_TIMEOUT = ClientExceptionProxy(lambda ex: ex.ServerTimeout) SSL_ERROR = ClientExceptionProxy(lambda ex: ex.SSLError) def modify_request(self, request_builder): request_builder.add_request_template( _RetryTemplate( self._when(request_builder), self._backoff, self._stop, ) )