diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | AUTHORS.rst | 3 | ||||
-rw-r--r-- | retrying.py | 25 | ||||
-rw-r--r-- | test_retrying.py | 34 |
4 files changed, 59 insertions, 6 deletions
@@ -1,4 +1,5 @@ .idea dist *.pyc -*.egg-info
\ No newline at end of file +*.egg-info +build diff --git a/AUTHORS.rst b/AUTHORS.rst index 9cb572f..42a456c 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -30,4 +30,5 @@ Patches and Suggestions - Monty Taylor - Maxym Shalenyi - Jonathan Herriott -- Job Evers
\ No newline at end of file +- Job Evers +- Cyrus Durgin diff --git a/retrying.py b/retrying.py index 32b8db6..bcb7a9d 100644 --- a/retrying.py +++ b/retrying.py @@ -68,13 +68,16 @@ class Retrying(object): wait_fixed=None, wait_random_min=None, wait_random_max=None, wait_incrementing_start=None, wait_incrementing_increment=None, + wait_incrementing_max=None, wait_exponential_multiplier=None, wait_exponential_max=None, retry_on_exception=None, retry_on_result=None, wrap_exception=False, stop_func=None, wait_func=None, - wait_jitter_max=None): + wait_jitter_max=None, + before_attempts=None, + after_attempts=None): self._stop_max_attempt_number = 5 if stop_max_attempt_number is None else stop_max_attempt_number self._stop_max_delay = 100 if stop_max_delay is None else stop_max_delay @@ -85,7 +88,10 @@ class Retrying(object): self._wait_incrementing_increment = 100 if wait_incrementing_increment is None else wait_incrementing_increment self._wait_exponential_multiplier = 1 if wait_exponential_multiplier is None else wait_exponential_multiplier self._wait_exponential_max = MAX_WAIT if wait_exponential_max is None else wait_exponential_max + self._wait_incrementing_max = MAX_WAIT if wait_incrementing_max is None else wait_incrementing_max self._wait_jitter_max = 0 if wait_jitter_max is None else wait_jitter_max + self._before_attempts = before_attempts + self._after_attempts = after_attempts # TODO add chaining of stop behaviors # stop behavior @@ -157,7 +163,8 @@ class Retrying(object): """Stop after the time from the first attempt >= stop_max_delay.""" return delay_since_first_attempt_ms >= self._stop_max_delay - def no_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + @staticmethod + def no_sleep(previous_attempt_number, delay_since_first_attempt_ms): """Don't sleep at all before retrying.""" return 0 @@ -175,6 +182,8 @@ class Retrying(object): wait_incrementing_start and incrementing by wait_incrementing_increment """ result = self._wait_incrementing_start + (self._wait_incrementing_increment * (previous_attempt_number - 1)) + if result > self._wait_incrementing_max: + result = self._wait_incrementing_max if result < 0: result = 0 return result @@ -188,10 +197,12 @@ class Retrying(object): result = 0 return result - def never_reject(self, result): + @staticmethod + def never_reject(result): return False - def always_reject(self, result): + @staticmethod + def always_reject(result): return True def should_reject(self, attempt): @@ -207,6 +218,9 @@ class Retrying(object): start_time = int(round(time.time() * 1000)) attempt_number = 1 while True: + if self._before_attempts: + self._before_attempts(attempt_number) + try: attempt = Attempt(fn(*args, **kwargs), attempt_number, False) except: @@ -216,6 +230,9 @@ class Retrying(object): if not self.should_reject(attempt): return attempt.get(self._wrap_exception) + if self._after_attempts: + self._after_attempts(attempt_number) + delay_since_first_attempt_ms = int(round(time.time() * 1000)) - start_time if self.stop(attempt_number, delay_since_first_attempt_ms): if not self._wrap_exception and attempt.has_exception: diff --git a/test_retrying.py b/test_retrying.py index 4c30893..8ce4ac3 100644 --- a/test_retrying.py +++ b/test_retrying.py @@ -434,5 +434,39 @@ class TestDecoratorWrapper(unittest.TestCase): self.assertTrue(_retryable_default(NoCustomErrorAfterCount(5))) self.assertTrue(_retryable_default_f(NoCustomErrorAfterCount(5))) +class TestBeforeAfterAttempts(unittest.TestCase): + _attempt_number = 0 + + def test_before_attempts(self): + TestBeforeAfterAttempts._attempt_number = 0 + + def _before(attempt_number): + TestBeforeAfterAttempts._attempt_number = attempt_number + + @retry(wait_fixed = 1000, stop_max_attempt_number = 1, before_attempts = _before) + def _test_before(): + pass + + _test_before() + + self.assertTrue(TestBeforeAfterAttempts._attempt_number is 1) + + def test_after_attempts(self): + TestBeforeAfterAttempts._attempt_number = 0 + + def _after(attempt_number): + TestBeforeAfterAttempts._attempt_number = attempt_number + + @retry(wait_fixed = 100, stop_max_attempt_number = 3, after_attempts = _after) + def _test_after(): + if TestBeforeAfterAttempts._attempt_number < 2: + raise Exception("testing after_attempts handler") + else: + pass + + _test_after() + + self.assertTrue(TestBeforeAfterAttempts._attempt_number is 2) + if __name__ == '__main__': unittest.main() |