From 10bb88e75e83618fa1ef8447e9feb38f4fa070bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl> Date: Wed, 18 Aug 2021 16:03:44 +0200 Subject: [PATCH] add a parameter to launch, v2.17.18 --- CHANGELOG.md | 3 +++ satella/__init__.py | 2 +- satella/time/backoff.py | 27 +++++++++++++++++++-------- tests/test_time.py | 24 ++++++++++++++++++++++-- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7125219..caa704b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,4 @@ # v2.17.18 + +* added parameter immediate to ExponentialBackoff.launch +* fixed a bug with ExponentialBackoff.launch diff --git a/satella/__init__.py b/satella/__init__.py index 487940e3..f5ef4834 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.17.18a1' +__version__ = '2.17.18' diff --git a/satella/time/backoff.py b/satella/time/backoff.py index 68c3583b..ec54ee92 100644 --- a/satella/time/backoff.py +++ b/satella/time/backoff.py @@ -129,7 +129,8 @@ class ExponentialBackoff: self.unavailable_until = None self.condition.notify_all() - def launch(self, exceptions_on_failed: ExceptionList = Exception): + def launch(self, exceptions_on_failed: ExceptionList = Exception, + immediate: bool = False): """ A decorator to simplify writing doing-something loops. Basically, this: @@ -149,19 +150,29 @@ class ExponentialBackoff: >>> eb.failed() >>> eb.sleep() + The first example with :code:`immediate=True` could skip the last call to do_action, + as it will be executed automatically with zero parameters if immediate=True is set. + :param exceptions_on_failed: a list of a single exception of exceptions whose raising will signal that fun has failed + :param immediate: immediately execute the function, but return the wrapped function + as a result of this decorator. The function will be called with zero arguments. :return: a function, that called, will pass the exactly same parameters """ def outer(fun): @wraps(fun) def inner(*args, **kwargs): - try: - r = fun(*args, **kwargs) - self.success() - return r - except exceptions_on_failed: - self.failed() - self.sleep() + while True: + try: + r = fun(*args, **kwargs) + self.success() + return r + except exceptions_on_failed: + self.failed() + self.sleep() + if immediate: + inner() + return inner + return outer diff --git a/tests/test_time.py b/tests/test_time.py index b33fb42f..a862aecc 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -38,13 +38,32 @@ class TestTime(unittest.TestCase): time.sleep(1) eb.success() + def test_exponential_backoff_launch_immediate(self): + eb = ExponentialBackoff(start=2, limit=30) + i = 1 + a = 0 + with measure() as m: + @eb.launch(ValueError, immediate=True) + def do_action(): + nonlocal i, a + a += 1 + if i == 4: + return + else: + i += 1 + raise ValueError() + self.assertGreaterEqual(m(), 2) + self.assertGreaterEqual(a, 2) + def test_exponential_backoff_launch(self): eb = ExponentialBackoff(start=2, limit=30) i = 1 + a = 0 @eb.launch(ValueError) def do_action(): - nonlocal i - if i == 3: + nonlocal i, a + a += 1 + if i == 4: return else: i += 1 @@ -53,6 +72,7 @@ class TestTime(unittest.TestCase): with measure() as m: do_action() self.assertGreaterEqual(m(), 2) + self.assertGreaterEqual(a, 2) def test_exponential_backoff_waiting_for_service_healthy(self): eb = ExponentialBackoff(start=2, limit=30) -- GitLab