From 2d8bbacb3828dce07a0a2eb7fc1700f95b442b14 Mon Sep 17 00:00:00 2001 From: Luca Castoro Date: Tue, 13 Nov 2018 14:49:07 +0100 Subject: [PATCH 1/4] Now the string representation of an Attempt contains a meaningful message that can be used to trace back the failing function without inpsecting the back trace. The message can be specified using the @retry(custom_message='...') argument, and if omitted will default to the name of the function invoked. Unit tests updated accordingly. --- retrying.py | 16 ++++++++++------ test_retrying.py | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/retrying.py b/retrying.py index bcb7a9d..6723520 100644 --- a/retrying.py +++ b/retrying.py @@ -77,7 +77,8 @@ def __init__(self, wait_func=None, wait_jitter_max=None, before_attempts=None, - after_attempts=None): + after_attempts=None, + custom_message=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 @@ -92,6 +93,7 @@ def __init__(self, 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 + self._custom_message = custom_message # TODO add chaining of stop behaviors # stop behavior @@ -217,15 +219,16 @@ def should_reject(self, attempt): def call(self, fn, *args, **kwargs): start_time = int(round(time.time() * 1000)) attempt_number = 1 + message = self._custom_message if self._custom_message else fn.__name__ while True: if self._before_attempts: self._before_attempts(attempt_number) try: - attempt = Attempt(fn(*args, **kwargs), attempt_number, False) + attempt = Attempt(fn(*args, **kwargs), attempt_number, False, message) except: tb = sys.exc_info() - attempt = Attempt(tb, attempt_number, True) + attempt = Attempt(tb, attempt_number, True, message) if not self.should_reject(attempt): return attempt.get(self._wrap_exception) @@ -257,10 +260,11 @@ class Attempt(object): occurred during the execution. """ - def __init__(self, value, attempt_number, has_exception): + def __init__(self, value, attempt_number, has_exception, message): self.value = value self.attempt_number = attempt_number self.has_exception = has_exception + self.message = message def get(self, wrap_exception=False): """ @@ -278,9 +282,9 @@ def get(self, wrap_exception=False): def __repr__(self): if self.has_exception: - return "Attempts: {0}, Error:\n{1}".format(self.attempt_number, "".join(traceback.format_tb(self.value[2]))) + return "Attempts: {0}, {1}, Error:\n{2}".format(self.attempt_number, self.message, "".join(traceback.format_tb(self.value[2]))) else: - return "Attempts: {0}, Value: {1}".format(self.attempt_number, self.value) + return "Attempts: {0}, {1}, Value: {2}".format(self.attempt_number, self.message, self.value) class RetryError(Exception): diff --git a/test_retrying.py b/test_retrying.py index 8ce4ac3..25ee6dc 100644 --- a/test_retrying.py +++ b/test_retrying.py @@ -468,5 +468,19 @@ def _test_after(): self.assertTrue(TestBeforeAfterAttempts._attempt_number is 2) +class TestMeaningfulMessage(unittest.TestCase): + + def test_function_name(self): + @retry(retry_on_result = lambda x: not x, stop_max_attempt_number = 5) + def just_a_function(): + return 0 + self.assertRaisesRegexp(RetryError, 'just_a_function', just_a_function) + + def test_custom_message(self): + @retry(retry_on_result = lambda x: not x, stop_max_attempt_number = 5, custom_message = 'hello world') + def just_a_function(): + return 0 + self.assertRaisesRegexp(RetryError, 'hello world', just_a_function) + if __name__ == '__main__': unittest.main() From ce83617b3c66acaca05121acd028416f689834ff Mon Sep 17 00:00:00 2001 From: Luca Castoro <13522333+lucacastoro@users.noreply.github.com> Date: Tue, 13 Nov 2018 14:59:40 +0100 Subject: [PATCH 2/4] Update AUTHORS.rst Added myself to the AUTHORS.rst file :smile: --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 42a456c..81fcfc2 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -32,3 +32,4 @@ Patches and Suggestions - Jonathan Herriott - Job Evers - Cyrus Durgin +- Luca Castoro From 39bd54818125f4eebd2851e0e328596b477cb06e Mon Sep 17 00:00:00 2001 From: Luca Castoro Date: Tue, 13 Nov 2018 19:13:08 +0100 Subject: [PATCH 3/4] Reverted from unittest.assertRaisesRegexp() to a simpler try/except block to be compatible with python <2.7 --- test_retrying.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test_retrying.py b/test_retrying.py index 25ee6dc..860739a 100644 --- a/test_retrying.py +++ b/test_retrying.py @@ -12,6 +12,7 @@ ## See the License for the specific language governing permissions and ## limitations under the License. +import sys import time import unittest @@ -471,16 +472,28 @@ def _test_after(): class TestMeaningfulMessage(unittest.TestCase): def test_function_name(self): + @retry(retry_on_result = lambda x: not x, stop_max_attempt_number = 5) def just_a_function(): return 0 - self.assertRaisesRegexp(RetryError, 'just_a_function', just_a_function) + + try: + just_a_function() + self.assertFalse(True) + except RetryError as ex: + self.assertIn('just_a_function', str(ex)) def test_custom_message(self): + @retry(retry_on_result = lambda x: not x, stop_max_attempt_number = 5, custom_message = 'hello world') def just_a_function(): return 0 - self.assertRaisesRegexp(RetryError, 'hello world', just_a_function) + + try: + just_a_function() + self.assertFalse(True) + except RetryError as ex: + self.assertIn('hello world', str(ex)) if __name__ == '__main__': unittest.main() From 66faf08a2e8389438f80e2cf726361549ffcadf4 Mon Sep 17 00:00:00 2001 From: Luca Castoro Date: Tue, 13 Nov 2018 19:17:47 +0100 Subject: [PATCH 4/4] Reverted also unittest.assertIn() assertTrue(x in y) to be backward compatible with python <2.7 --- test_retrying.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_retrying.py b/test_retrying.py index 860739a..722d398 100644 --- a/test_retrying.py +++ b/test_retrying.py @@ -481,7 +481,7 @@ def just_a_function(): just_a_function() self.assertFalse(True) except RetryError as ex: - self.assertIn('just_a_function', str(ex)) + self.assertTrue('just_a_function' in str(ex)) def test_custom_message(self): @@ -493,7 +493,7 @@ def just_a_function(): just_a_function() self.assertFalse(True) except RetryError as ex: - self.assertIn('hello world', str(ex)) + self.assertTrue('hello world' in str(ex)) if __name__ == '__main__': unittest.main()