diff --git a/AUTHORS b/AUTHORS index 3ae1286..f49b39a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,3 +3,4 @@ Rémy Greinhofer invlpg Richard O'Dwyer williara +n0npax diff --git a/README.rst b/README.rst index e1d0e9c..9372dc0 100644 --- a/README.rst +++ b/README.rst @@ -50,6 +50,7 @@ retry decorator 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 instant_raise_exceptions: an exception or tuple which will be raised without retry """ Various retrying logic can be achieved by combination of arguments. @@ -124,6 +125,7 @@ retry_call 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 instant_raise_exceptions: an exception or tuple which will be raised without retry :returns: the result of the f function. """ diff --git a/retry/api.py b/retry/api.py index 4a404b9..d3d1bc8 100644 --- a/retry/api.py +++ b/retry/api.py @@ -11,7 +11,7 @@ def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, - logger=logging_logger): + logger=logging_logger, instant_raise_exceptions=None): """ Executes a function and retries it if it failed. @@ -25,12 +25,15 @@ def __retry_internal(f, exceptions=Exception, tries=-1, delay=0, max_delay=None, 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 instant_raise_exceptions: an exception or tuple which will be raised without retry :returns: the result of the f function. """ _tries, _delay = tries, delay while _tries: try: return f() + except instant_raise_exceptions: + raise except exceptions as e: _tries -= 1 if not _tries: @@ -51,7 +54,8 @@ 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, logger=logging_logger, + instant_raise_exceptions=None): """Returns a retry decorator. :param exceptions: an exception or a tuple of exceptions to catch. default: Exception. @@ -63,6 +67,7 @@ def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, ji 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 instant_raise_exceptions: an exception or tuple which will be raised without retry :returns: a retry decorator. """ @@ -71,14 +76,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) + logger, instant_raise_exceptions) 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): + jitter=0, logger=logging_logger, instant_raise_exceptions=None): """ Calls a function and re-executes it if it failed. @@ -94,8 +98,10 @@ def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, dela 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 instant_raise_exceptions: an exception or tuple which will be raised without retry :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) + return __retry_internal(partial(f, *args, **kwargs), exceptions, tries, delay, max_delay, backoff, jitter, logger, + instant_raise_exceptions) diff --git a/tests/test_retry.py b/tests/test_retry.py index 64f45cd..28ca4db 100644 --- a/tests/test_retry.py +++ b/tests/test_retry.py @@ -70,6 +70,38 @@ def f(): assert f() == target +def test_instant_raise_exceptions(): + + class MyException(Exception): pass + hit = [0] + + @retry(instant_raise_exceptions=MyException) + def f(): + hit[0] += 1 + raise MyException + + with pytest.raises(MyException): + f() + assert hit[0] == 1 + + +def test_exceptions_and_instant_raise_exceptions(): + + class MyException(Exception): pass + hit = [0] + + @retry(instant_raise_exceptions=MyException) + def f(): + hit[0] += 1 + if hit[0] > 5: + raise MyException + else: + raise IOError + with pytest.raises(MyException): + f() + assert hit[0] == 6 + + def test_max_delay(monkeypatch): mock_sleep_time = [0]