From b0b686e546f0f982ea2e20ff2655d610828a5af9 Mon Sep 17 00:00:00 2001 From: Tomer Shalev Date: Thu, 29 Jul 2021 20:49:16 +0300 Subject: [PATCH] Add option to show traceback after each failed retry --- README.rst | 2 ++ retry/api.py | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index e1d0e9c..5383210 100644 --- a/README.rst +++ b/README.rst @@ -48,6 +48,7 @@ retry decorator :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 show_traceback: Print traceback before retrying (Python3 only). default: False. :param logger: logger.warning(fmt, error, delay) will be called on failed attempts. default: retry.logging_logger. if None, logging is disabled. """ @@ -122,6 +123,7 @@ retry_call :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 show_traceback: Print traceback before retrying (Python3 only). default: False. :param logger: logger.warning(fmt, error, delay) will be called on failed attempts. default: retry.logging_logger. if None, logging is disabled. :returns: the result of the f function. diff --git a/retry/api.py b/retry/api.py index 4a404b9..aa86780 100644 --- a/retry/api.py +++ b/retry/api.py @@ -1,17 +1,17 @@ import logging import random +import sys 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): + show_traceback=False, logger=logging_logger): """ Executes a function and retries it if it failed. @@ -36,7 +36,13 @@ def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, if not _tries: raise + assert not show_traceback or logger is not None, 'Show traceback needs logger' + assert not show_traceback or sys.version_info[0] != 2, 'Traceback not supported in Python2' if logger is not None: + assert not show_traceback, 'fooooo' + if show_traceback: + tb_str = ''.join(traceback.format_exception(None, e, e.__traceback__)) + logger.warning(tb_str) logger.warning('%s, retrying in %s seconds...', e, _delay) time.sleep(_delay) @@ -51,7 +57,7 @@ def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, _delay = min(_delay, max_delay) -def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger): +def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, show_traceback=False, logger=logging_logger): """Returns a retry decorator. :param exceptions: an exception or a tuple of exceptions to catch. default: Exception. @@ -71,13 +77,13 @@ 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) + show_traceback, logger) return retry_decorator def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, - jitter=0, + jitter=0, show_traceback=False, logger=logging_logger): """ Calls a function and re-executes it if it failed. @@ -98,4 +104,4 @@ def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, dela """ 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) + return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter, show_traceback, logger)