From d9a6f8de749f0f3b9e8fbe0466c0789c95923a34 Mon Sep 17 00:00:00 2001 From: Barry Coleman Date: Tue, 9 Aug 2022 08:50:07 -0700 Subject: [PATCH] Add excluded list for exceptions --- awsretry/__init__.py | 12 +++++++++--- tests/test_awsretry.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/awsretry/__init__.py b/awsretry/__init__.py index 329f6fc..8462891 100644 --- a/awsretry/__init__.py +++ b/awsretry/__init__.py @@ -47,7 +47,7 @@ def found(response_code): pass @classmethod - def backoff(cls, tries=10, delay=3, backoff=1.1, added_exceptions=list()): + def backoff(cls, tries=10, delay=3, backoff=1.1, added_exceptions=list(), excluded_exceptions=list()): """ Retry calling the Cloud decorated function using an exponential backoff. Kwargs: @@ -60,6 +60,8 @@ def backoff(cls, tries=10, delay=3, backoff=1.1, added_exceptions=list()): default=2 added_exceptions (list): Other exceptions to retry on. default=[] + excluded_exceptions (list): Exceptions not to retry on. + default=[] """ def deco(f): @@ -73,7 +75,7 @@ def retry_func(*args, **kwargs): base_exception_class = cls.base_class(e) if isinstance(e, base_exception_class): response_code = cls.status_code_from_exception(e) - if cls.found(response_code, added_exceptions): + if cls.found(response_code, added_exceptions, excluded_exceptions): logging.info("{0}: Retrying in {1} seconds...".format(str(e), max_delay)) time.sleep(max_delay) max_tries -= 1 @@ -119,7 +121,11 @@ def status_code_from_exception(error): return error.__class__.__name__ @staticmethod - def found(response_code, added_exceptions): + def found(response_code, added_exceptions, excluded_exceptions): + # If the response_code is in the excluded list return immediately + if response_code in excluded_exceptions: + return False + # This list of failures is based on this API Reference # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html retry_on = [ diff --git a/tests/test_awsretry.py b/tests/test_awsretry.py index e344a6b..c93330a 100644 --- a/tests/test_awsretry.py +++ b/tests/test_awsretry.py @@ -62,6 +62,48 @@ def raise_unexpected_error(): self.assertEqual(self.counter, 1) + def test_excluded_exception_does_not_retry(self): + self.counter = 0 + err_msg = {'Error': {'Code': 'ResourceNotFoundException'}} + + @AWSRetry.backoff(tries=4, delay=0.1, excluded_exceptions=['ResourceNotFoundException']) + def raise_excluded_error(): + self.counter += 1 + raise botocore.exceptions.ClientError(err_msg, 'excluded') + + with self.assertRaises(botocore.exceptions.ClientError): + raise_excluded_error() + + self.assertEqual(self.counter, 1) + + def test_excluded_exception_retry_for_non_match(self): + self.counter = 0 + err_msg = {'Error': {'Code': 'InstanceId.NotFound'}} + + @AWSRetry.backoff(tries=4, delay=0.1, excluded_exceptions=['ResourceNotFoundException']) + def raise_excluded_error_no_match(): + self.counter += 1 + raise botocore.exceptions.ClientError(err_msg, 'excluded_not_match') + + with self.assertRaises(botocore.exceptions.ClientError): + raise_excluded_error_no_match() + + self.assertEqual(self.counter, 4) + + def test_excluded_core_exception_does_not_retry(self): + self.counter = 0 + err_msg = {'Error': {'Code': 'InternalFailure'}} + + @AWSRetry.backoff(tries=4, delay=0.1, excluded_exceptions=['InternalFailure']) + def raise_excluded_core_error(): + self.counter += 1 + raise botocore.exceptions.ClientError(err_msg, 'excluded_core') + + with self.assertRaises(botocore.exceptions.ClientError): + raise_excluded_core_error() + + self.assertEqual(self.counter, 1) + if __name__ == '__main__': unittest.main()