summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--AUTHORS.rst3
-rw-r--r--retrying.py25
-rw-r--r--test_retrying.py34
4 files changed, 59 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore
index ec62bd6..2e34cff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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()