import logging import random import time import traceback from functools import partial from .compat import decorator logging_logger = logging.getLogger(__name__) def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger, log_traceback=False, on_exception=None): """ Executes a function and retries it if it failed. :param f: the function to execute. :param exceptions: an exception or a tuple of exceptions to catch. default: Exception. :param tries: the maximum number of attempts. default: -1 (infinite). :param delay: initial delay between attempts. default: 0. :param max_delay: the maximum value of delay. default: None (no limit). :param backoff: multiplier applied to delay between attempts. default: 1 (no backoff). :param jitter: extra seconds added to delay between attempts. default: 0. fixed if a number, random if a range tuple (min, max) :param logger: logger.warning(fmt, error, delay) will be called on failed attempts. default: retry.logging_logger. if None, logging is disabled. :param on_exception: handler called when exception occurs. will be passed the captured exception as an argument. further retries are stopped when handler returns True. default: None :returns: the result of the f function. """ _tries, _delay = tries, delay while _tries: try: return f() except exceptions as e: if on_exception is not None: if on_exception(e): break _tries -= 1 if not _tries: raise if logger is not None: try: func_qualname = f.func.__qualname__ except AttributeError: func_qualname = str(f.func) logger.warning('{}: {} in {}.{}, retrying in {} seconds...'.format(e.__class__.__qualname__, e, f.func.__module__, func_qualname, _delay)) if log_traceback: logger.warning(traceback.format_exc()) time.sleep(_delay) _delay *= backoff if isinstance(jitter, tuple): _delay += random.uniform(*jitter) else: _delay += jitter if max_delay is not None: _delay = min(_delay, max_delay) def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger, log_traceback=False, on_exception=None): """Returns a retry decorator. :param exceptions: an exception or a tuple of exceptions to catch. default: Exception. :param tries: the maximum number of attempts. default: -1 (infinite). :param delay: initial delay between attempts. default: 0. :param max_delay: the maximum value of delay. default: None (no limit). :param backoff: multiplier applied to delay between attempts. default: 1 (no backoff). :param jitter: extra seconds added to delay between attempts. default: 0. fixed if a number, random if a range tuple (min, max) :param logger: logger.warning(fmt, error, delay) will be called on failed attempts. default: retry.logging_logger. if None, logging is disabled. :param on_exception: handler called when exception occurs. will be passed the captured exception as an argument. further retries are stopped when handler returns True. default: None :returns: a retry decorator. """ @decorator def retry_decorator(f, *fargs, **fkwargs): args = fargs if fargs else list() kwargs = fkwargs if fkwargs else dict() return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter, logger, log_traceback, on_exception) return retry_decorator def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger, log_traceback=False, on_exception=None): """ Calls a function and re-executes it if it failed. :param f: the function to execute. :param fargs: the positional arguments of the function to execute. :param fkwargs: the named arguments of the function to execute. :param exceptions: an exception or a tuple of exceptions to catch. default: Exception. :param tries: the maximum number of attempts. default: -1 (infinite). :param delay: initial delay between attempts. default: 0. :param max_delay: the maximum value of delay. default: None (no limit). :param backoff: multiplier applied to delay between attempts. default: 1 (no backoff). :param jitter: extra seconds added to delay between attempts. default: 0. fixed if a number, random if a range tuple (min, max) :param logger: logger.warning(fmt, error, delay) will be called on failed attempts. default: retry.logging_logger. if None, logging is disabled. :param on_exception: handler called when exception occurs. will be passed the captured exception as an argument. further retries are stopped when handler returns True. default: None :returns: the result of the f function. """ args = fargs if fargs else list() kwargs = fkwargs if fkwargs else dict() return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter, logger, log_traceback, on_exception)