Search code examples
pythonpython-decorators

How to have retry decorator indicate used all retries?


I have implemented the retry decorator in my code, but would like to somehow indicate when it has used all of its retries. How can I do this?

I am using retrying decorator v1.3.3.

I tried using stop_func, but this seems to be called in nominal behaviour, not on a retry.

I am not sure how to call out the attempt number from the decorator.

from retrying import retry

def _query_with_retries(self):
    _retriable_query = retry(stop_max_attempt_number=3,
                             wait_incrementing_start=50,
                             wait_incrementing_increment=10)(self._query)
    return _retriable_query()

Currently, my code just throws a generic exception on the last retry. I would like to be able to message "all retries have been used up" or something like that.


Solution

  • You can catch the retrying.RetryError exception that is raised when the decorator stops retrying; do so in your own wrapper decorator:

    from functools import wraps
    from retrying import retry, RetryError
    
    def printing_retry(*args, **kwargs):
        def decorator(f):            
            decorated = retry(*args, **kwargs)(f)
            @wraps(decorated)
            def wrapper(*args, **kwargs):
                try:
                    return decorated(*args, **kwargs)
                except RetryError:
                    print("All retries have been used up")
                    # optionally, re-raise the exception at this point
                    # raise
            return wrapper
        if len(args) == 1 and callable(args[0]):
            return decorator(args[0])
        return decorator
    

    This decorator would replace the @retry decorators in your code; when you call a decorated function, it'll catch the RetryError exception that is raised when the function has run out of attempts, and print out a message instead.

    Do remember to set wrap_exception=True if you want to wrap any exceptions raised during retrying in a RetryException exception.

    Demo:

    >>> @printing_retry(stop_max_attempt_number=1, wrap_exception=True)
    ... def do_something_unreliable():
    ...     if random.randint(0, 10) > 1:
    ...         raise IOError("Broken sauce, everything is hosed!!!111one")
    ...     else:
    ...         return "Awesome sauce!"
    ...
    >>> do_something_unreliable()
    All retries have been used up
    >>> do_something_unreliable()
    'Awesome sauce!'