From f528c276f8e347465ded927de4523bda4034a89d Mon Sep 17 00:00:00 2001 From: James Saryerwinnie Date: Mon, 26 Jan 2015 13:38:44 -0800 Subject: [PATCH] Add compat for legacy operation methods on waiters While pulling in https://github.com/boto/botocore/pull/437, I noticed that we aren't handling the 'error' matcher properly because of a CLI customization that will raise an error on non 200 responses. This handles that case so, for example, the command `aws rds wait db-instance-deleted --db-instance-identifier foo` will work as expected. Previously, the error was being propogated and the CLI command would exit with a non-zero RC and a client error message. --- botocore/waiter.py | 24 ++++++++++++++++++++++-- tests/unit/test_waiters.py | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/botocore/waiter.py b/botocore/waiter.py index b49c9a19b0..fbc425ecf9 100644 --- a/botocore/waiter.py +++ b/botocore/waiter.py @@ -96,10 +96,28 @@ def __init__(self, operation_object, endpoint): self._endpoint = endpoint def __call__(self, **kwargs): - http, parsed = self._operation_object.call( - self._endpoint, **kwargs) + try: + http, parsed = self._operation_object.call( + self._endpoint, **kwargs) + except Exception as e: + # In theory, a handler can raise an type of exception. + # We're going to make a best effort attempt to handle + # the ClientError attributes, but not require that + # the exception is an instance of ClientError. + if self._looks_like_client_error(e): + return { + 'Error': { + 'Code': e.error_code, + 'Message': e.error_message, + } + } + else: + raise return parsed + def _looks_like_client_error(self, e): + return hasattr(e, 'error_code') and hasattr(e, 'error_message') + class WaiterModel(object): SUPPORTED_VERSION = 2 @@ -317,6 +335,8 @@ def wait(self, **kwargs): raise WaiterError(name=self.name, reason='Unexpected error encountered.') if current_state == 'success': + logger.debug("Waiting complete, waiter matched the " + "success state.") return if current_state == 'failure': raise WaiterError( diff --git a/tests/unit/test_waiters.py b/tests/unit/test_waiters.py index a1a74468dd..4d11a33f04 100644 --- a/tests/unit/test_waiters.py +++ b/tests/unit/test_waiters.py @@ -508,6 +508,28 @@ def test_legacy_op_method_makes_call(self): operation_object.call.assert_called_with( endpoint, Foo='a', Bar='b') + def test_legacy_method_handles_exceptions(self): + operation_object = mock.Mock() + exception = Exception() + exception.error_message = 'Foo' + exception.error_code = 'MyCode' + operation_object.call.side_effect = exception + endpoint = mock.Mock() + op = LegacyOperationMethod(operation_object, endpoint) + response = op(Foo='a', Bar='b') + self.assertEqual(response, + {'Error': {'Code': 'MyCode', 'Message': 'Foo'}}) + + def test_legacy_method_with_unknown_exception(self): + operation_object = mock.Mock() + # A generic exception missing the error_message and error_code + # attrs will just be reraised. + operation_object.call.side_effect = ValueError + endpoint = mock.Mock() + op = LegacyOperationMethod(operation_object, endpoint) + with self.assertRaises(ValueError): + op(Foo='a', Bar='b') + class ServiceWaiterFunctionalTest(BaseEnvVar): """