Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

fixed a bug where classes not extending from the Python exception hie…

…rarchy could slip through, preparing for 1.0.1 release
  • Loading branch information...
commit 8035a32c1bb8f80150498017b1e35815b01790e9 1 parent a7a6a43
Ray Holder authored
Showing with 99 additions and 4 deletions.
  1. +5 −0 HISTORY.rst
  2. +3 −1 retrying.py
  3. +1 −1  setup.py
  4. +90 −2 test_retrying.py
5 HISTORY.rst
View
@@ -3,6 +3,11 @@
History
-------
+1.0.1 (2013-03-20)
+++++++++++++++++++
+- Fixed a bug where classes not extending from the Python exception hierarchy could slip through
+- Update test suite for custom Python exceptions
+
1.0.0 (2013-01-21)
++++++++++++++++++
- First stable, tested version now exists
4 retrying.py
View
@@ -13,6 +13,7 @@
## limitations under the License.
import random
+import sys
import time
# sys.maxint / 2, since Python 3.2 doesn't have a sys.maxint...
@@ -150,7 +151,8 @@ def call(self, fn, *args, **kwargs):
while True:
try:
attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
- except BaseException as e:
+ except:
+ e = sys.exc_info()[1]
attempt = Attempt(e, attempt_number, True)
if not self.should_reject(attempt):
2  setup.py
View
@@ -18,7 +18,7 @@
settings.update(
name='retrying',
- version='1.0.0',
+ version='1.0.1',
description='Retrying',
long_description=open('README.rst').read() + '\n\n' +
open('HISTORY.rst').read(),
92 test_retrying.py
View
@@ -162,6 +162,34 @@ def go(self):
raise NameError()
return True
+class CustomError:
+ """
+ This is a custom exception class that doesn't inherit from any of the Python base Exception hierarchy.
+ """
+
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
+
+class NoCustomErrorAfterCount:
+ """
+ This class holds counter state for invoking a method several times in a row.
+ """
+
+ def __init__(self, count):
+ self.counter = 0
+ self.count = count
+
+ def go(self):
+ """
+ Raise a CustomError until after count threshold has been crossed, then return True.
+ """
+ if self.counter < self.count:
+ self.counter += 1
+ raise CustomError("This is a Custom exception class")
+ return True
def retry_if_result_none(result):
return result is None
@@ -213,6 +241,29 @@ def _retryable_default(thing):
def _retryable_default_f(thing):
return thing.go()
+@retry(retry_on_exception=retry_if_exception_of_type(CustomError))
+def _retryable_test_with_exception_type_custom(thing):
+ return thing.go()
+
+@retry(retry_on_exception=retry_if_exception_of_type(CustomError), wrap_exception=True)
+def _retryable_test_with_exception_type_custom_wrap(thing):
+ return thing.go()
+
+@retry(
+ stop='stop_after_attempt',
+ stop_max_attempt_number=3,
+ retry_on_exception=retry_if_exception_of_type(CustomError))
+def _retryable_test_with_exception_type_custom_attempt_limit(thing):
+ return thing.go()
+
+@retry(
+ stop='stop_after_attempt',
+ stop_max_attempt_number=3,
+ retry_on_exception=retry_if_exception_of_type(CustomError),
+ wrap_exception=True)
+def _retryable_test_with_exception_type_custom_attempt_limit_wrap(thing):
+ return thing.go()
+
class TestDecoratorWrapper(unittest.TestCase):
def test_with_wait(self):
@@ -240,13 +291,31 @@ def test_retry_if_exception_of_type(self):
try:
_retryable_test_with_exception_type_io_attempt_limit(NoIOErrorAfterCount(5))
- self.fail("RetryError expected")
+ self.fail("Expected RetryError")
except RetryError as re:
self.assertEqual(3, re.last_attempt.attempt_number)
self.assertTrue(re.last_attempt.has_exception)
self.assertTrue(isinstance(re.last_attempt.value, IOError))
+ self.assertTrue(_retryable_test_with_exception_type_custom(NoCustomErrorAfterCount(5)))
+
+ try:
+ _retryable_test_with_exception_type_custom(NoNameErrorAfterCount(5))
+ self.fail("Expected NameError")
+ except NameError as n:
+ self.assertTrue(isinstance(n, NameError))
+
+ try:
+ _retryable_test_with_exception_type_custom_attempt_limit(NoCustomErrorAfterCount(5))
+ self.fail("Expected RetryError")
+ except RetryError as re:
+ self.assertEqual(3, re.last_attempt.attempt_number)
+ self.assertTrue(re.last_attempt.has_exception)
+ self.assertTrue(isinstance(re.last_attempt.value, CustomError))
+
def test_wrapped_exception(self):
+
+ # base exception cases
self.assertTrue(_retryable_test_with_exception_type_io_wrap(NoIOErrorAfterCount(5)))
try:
@@ -257,15 +326,34 @@ def test_wrapped_exception(self):
try:
_retryable_test_with_exception_type_io_attempt_limit_wrap(NoIOErrorAfterCount(5))
- self.fail("RetryError expected")
+ self.fail("Expected RetryError")
except RetryError as re:
self.assertEqual(3, re.last_attempt.attempt_number)
self.assertTrue(re.last_attempt.has_exception)
self.assertTrue(isinstance(re.last_attempt.value, IOError))
+ # custom error cases
+ self.assertTrue(_retryable_test_with_exception_type_custom_wrap(NoCustomErrorAfterCount(5)))
+
+ try:
+ _retryable_test_with_exception_type_custom_wrap(NoNameErrorAfterCount(5))
+ self.fail("Expected RetryError")
+ except RetryError as r:
+ self.assertTrue(isinstance(r.last_attempt.value, NameError))
+
+ try:
+ _retryable_test_with_exception_type_custom_attempt_limit_wrap(NoCustomErrorAfterCount(5))
+ self.fail("Expected RetryError")
+ except RetryError as re:
+ self.assertEqual(3, re.last_attempt.attempt_number)
+ self.assertTrue(re.last_attempt.has_exception)
+ self.assertTrue(isinstance(re.last_attempt.value, CustomError))
+
def test_defaults(self):
self.assertTrue(_retryable_default(NoNameErrorAfterCount(5)))
self.assertTrue(_retryable_default_f(NoNameErrorAfterCount(5)))
+ self.assertTrue(_retryable_default(NoCustomErrorAfterCount(5)))
+ self.assertTrue(_retryable_default_f(NoCustomErrorAfterCount(5)))
if __name__ == '__main__':
unittest.main()
Please sign in to comment.
Something went wrong with that request. Please try again.