From 54739ba53f1fe469932384667988304b9e0ba223 Mon Sep 17 00:00:00 2001 From: Sherif Nada Date: Sun, 18 Apr 2021 23:35:39 -0700 Subject: [PATCH 1/2] Add new generator function --- backoff/__init__.py | 2 +- backoff/_async.py | 4 ++-- backoff/_common.py | 12 ++++++------ backoff/_sync.py | 4 ++-- backoff/_wait_gen.py | 16 ++++++++++++++++ tests/test_wait_gen.py | 25 ++++++++++++++++++++----- 6 files changed, 47 insertions(+), 16 deletions(-) diff --git a/backoff/__init__.py b/backoff/__init__.py index fc00001..a8673e0 100644 --- a/backoff/__init__.py +++ b/backoff/__init__.py @@ -14,7 +14,7 @@ """ from backoff._decorator import on_predicate, on_exception from backoff._jitter import full_jitter, random_jitter -from backoff._wait_gen import constant, expo, fibo +from backoff._wait_gen import constant, expo, fibo, from_value __all__ = [ 'on_predicate', diff --git a/backoff/_async.py b/backoff/_async.py index 38cde8e..69f7f50 100644 --- a/backoff/_async.py +++ b/backoff/_async.py @@ -74,7 +74,7 @@ async def retry(*args, **kwargs): break try: - seconds = _next_wait(wait, jitter, elapsed, max_time_) + seconds = _next_wait(wait, ret, jitter, elapsed, max_time_) except StopIteration: await _call_handlers(on_giveup, *details, value=ret) break @@ -142,7 +142,7 @@ async def retry(*args, **kwargs): raise try: - seconds = _next_wait(wait, jitter, elapsed, max_time_) + seconds = _next_wait(wait, e, jitter, elapsed, max_time_) except StopIteration: await _call_handlers(on_giveup, *details) raise e diff --git a/backoff/_common.py b/backoff/_common.py index b76e579..2aa93f4 100644 --- a/backoff/_common.py +++ b/backoff/_common.py @@ -6,14 +6,12 @@ import traceback import warnings - # python 2.7 -> 3.x compatibility for str and unicode try: basestring except NameError: # pragma: python=3.5 basestring = str - # Use module-specific logger with a default null handler. _logger = logging.getLogger('backoff') _logger.addHandler(logging.NullHandler()) # pragma: no cover @@ -27,11 +25,13 @@ def _maybe_call(f, *args, **kwargs): def _init_wait_gen(wait_gen, wait_gen_kwargs): kwargs = {k: _maybe_call(v) for k, v in wait_gen_kwargs.items()} - return wait_gen(**kwargs) + initialized = wait_gen(**kwargs) + initialized.send(None) # Initialize with an empty send + return initialized -def _next_wait(wait, jitter, elapsed, max_time): - value = next(wait) +def _next_wait(wait, send_value, jitter, elapsed, max_time): + value = wait.send(send_value) try: if jitter is not None: seconds = jitter(value) @@ -64,7 +64,7 @@ def _prepare_logger(logger): # Configure handler list with user specified handler and optionally # with a default handler bound to the specified logger. def _config_handlers( - user_handlers, default_handler=None, logger=None, log_level=None + user_handlers, default_handler=None, logger=None, log_level=None ): handlers = [] if logger is not None: diff --git a/backoff/_sync.py b/backoff/_sync.py index 477765d..3838e97 100644 --- a/backoff/_sync.py +++ b/backoff/_sync.py @@ -51,7 +51,7 @@ def retry(*args, **kwargs): break try: - seconds = _next_wait(wait, jitter, elapsed, max_time_) + seconds = _next_wait(wait, ret, jitter, elapsed, max_time_) except StopIteration: _call_handlers(on_giveup, *details) break @@ -102,7 +102,7 @@ def retry(*args, **kwargs): raise try: - seconds = _next_wait(wait, jitter, elapsed, max_time_) + seconds = _next_wait(wait, e, jitter, elapsed, max_time_) except StopIteration: _call_handlers(on_giveup, *details) raise e diff --git a/backoff/_wait_gen.py b/backoff/_wait_gen.py index 3cb0e11..a62778f 100644 --- a/backoff/_wait_gen.py +++ b/backoff/_wait_gen.py @@ -3,6 +3,19 @@ import itertools +def from_value(parser): + """ Generator that is based on parsing the return value or thrown exception of the decorated method + + Args: + parser: a callable which takes as input the decorated + function's return value or thrown exception and + determines how long to wait + """ + value = yield + while True: + value = yield parser(value) + + def expo(base=2, factor=1, max_value=None): """Generator for exponential decay. @@ -13,6 +26,7 @@ def expo(base=2, factor=1, max_value=None): true exponential sequence exceeds this, the value of max_value will forever after be yielded. """ + yield # Advance past initial .send() call n = 0 while True: a = factor * base ** n @@ -31,6 +45,7 @@ def fibo(max_value=None): true fibonacci sequence exceeds this, the value of max_value will forever after be yielded. """ + yield # Advance past initial .send() call a = 1 b = 1 while True: @@ -47,6 +62,7 @@ def constant(interval=1): Args: interval: A constant value to yield or an iterable of such values. """ + yield # Advance past initial .send() call try: itr = iter(interval) except TypeError: diff --git a/tests/test_wait_gen.py b/tests/test_wait_gen.py index eb60416..5a9dd4e 100644 --- a/tests/test_wait_gen.py +++ b/tests/test_wait_gen.py @@ -4,30 +4,35 @@ def test_expo(): gen = backoff.expo() + gen.send(None) for i in range(9): - assert 2**i == next(gen) + assert 2 ** i == next(gen) def test_expo_base3(): gen = backoff.expo(base=3) + gen.send(None) for i in range(9): - assert 3**i == next(gen) + assert 3 ** i == next(gen) def test_expo_factor3(): gen = backoff.expo(factor=3) + gen.send(None) for i in range(9): - assert 3 * 2**i == next(gen) + assert 3 * 2 ** i == next(gen) def test_expo_base3_factor5(): gen = backoff.expo(base=3, factor=5) + gen.send(None) for i in range(9): - assert 5 * 3**i == next(gen) + assert 5 * 3 ** i == next(gen) def test_expo_max_value(): - gen = backoff.expo(max_value=2**4) + gen = backoff.expo(max_value=2 ** 4) + gen.send(None) expected = [1, 2, 4, 8, 16, 16, 16] for expect in expected: assert expect == next(gen) @@ -35,6 +40,7 @@ def test_expo_max_value(): def test_fibo(): gen = backoff.fibo() + gen.send(None) expected = [1, 1, 2, 3, 5, 8, 13] for expect in expected: assert expect == next(gen) @@ -42,6 +48,7 @@ def test_fibo(): def test_fibo_max_value(): gen = backoff.fibo(max_value=8) + gen.send(None) expected = [1, 1, 2, 3, 5, 8, 8, 8] for expect in expected: assert expect == next(gen) @@ -49,5 +56,13 @@ def test_fibo_max_value(): def test_constant(): gen = backoff.constant(interval=3) + gen.send(None) for i in range(9): assert 3 == next(gen) + + +def test_from_value(): + gen = backoff.from_value(lambda x: x) + gen.send(None) + for i in range(20): + assert i == gen.send(i) From 7e1bc195562587ee1abf60edd9d310339b241027 Mon Sep 17 00:00:00 2001 From: Sherif Nada Date: Sun, 18 Apr 2021 23:50:04 -0700 Subject: [PATCH 2/2] style --- backoff/__init__.py | 1 + backoff/_wait_gen.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/backoff/__init__.py b/backoff/__init__.py index a8673e0..91ed99e 100644 --- a/backoff/__init__.py +++ b/backoff/__init__.py @@ -22,6 +22,7 @@ 'constant', 'expo', 'fibo', + 'from_value', 'full_jitter', 'random_jitter' ] diff --git a/backoff/_wait_gen.py b/backoff/_wait_gen.py index a62778f..1acee6f 100644 --- a/backoff/_wait_gen.py +++ b/backoff/_wait_gen.py @@ -4,7 +4,8 @@ def from_value(parser): - """ Generator that is based on parsing the return value or thrown exception of the decorated method + """ Generator that is based on parsing the return value or thrown + exception of the decorated method Args: parser: a callable which takes as input the decorated