Skip to content

Commit 2fb1902

Browse files
committed
A WIP commit on an event-based error handler. Needs tests, etc. but would appreciate review of basic approach.
1 parent 2c2d307 commit 2fb1902

4 files changed

Lines changed: 74 additions & 45 deletions

File tree

awscli/clidriver.py

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
from awscli.arguments import BooleanArgument
3333
from awscli.arguments import CLIArgument
3434
from awscli.arguments import UnknownArgumentError
35-
from awscli.arguments import BadArgumentError
3635

3736

3837
LOG = logging.getLogger('awscli.clidriver')
@@ -472,7 +471,14 @@ def _create_operation_parser(self, arg_table):
472471

473472

474473
class CLIOperationCaller(object):
475-
"""Call an AWS operation and format the response."""
474+
"""
475+
Call an AWS operation and format the response.
476+
477+
This class handles the non-error path. If an HTTP error occurs
478+
on the call to the service operation, it will be detected and
479+
handled by the :class:`awscli.errorhandler.ErrorHandler` which
480+
is registered on the ``after-call`` event.
481+
"""
476482

477483
def __init__(self, session):
478484
self._session = session
@@ -492,7 +498,6 @@ def invoke(self, operation_object, parameters, parsed_globals):
492498
**parameters)
493499
self._display_response(operation_object, response_data,
494500
parsed_globals)
495-
return self._handle_http_response(http_response, response_data)
496501

497502
def _display_response(self, operation, response, args):
498503
output = args.output
@@ -501,33 +506,3 @@ def _display_response(self, operation, response, args):
501506
formatter = get_formatter(output, args)
502507
formatter(operation, response)
503508

504-
def _handle_http_response(self, http_response, response_data):
505-
if http_response.status_code >= 500:
506-
msg = self._session.get_data('messages/ServerError')
507-
code, message = self._get_error_code_and_message(response_data)
508-
sys.stderr.write(msg.format(error_code=code,
509-
error_message=message))
510-
sys.stderr.write('\n')
511-
return http_response.status_code - 399
512-
if http_response.status_code >= 400:
513-
msg = self._session.get_data('messages/ClientError')
514-
code, message = self._get_error_code_and_message(response_data)
515-
sys.stderr.write(msg.format(error_code=code,
516-
error_message=message))
517-
sys.stderr.write('\n')
518-
return http_response.status_code - 399
519-
return 0
520-
521-
def _get_error_code_and_message(self, response):
522-
code = 'Unknown'
523-
message = 'Unknown'
524-
if 'Errors' in response:
525-
if isinstance(response['Errors'], list):
526-
error = response['Errors'][-1]
527-
if 'Code' in error:
528-
code = error['Code']
529-
elif 'Type' in error:
530-
code = error['Type']
531-
if 'Message' in error:
532-
message = error['Message']
533-
return (code, message)

awscli/data/messages.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

awscli/errorhandler.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
14+
import sys
15+
import logging
16+
17+
LOG = logging.getLogger(__name__)
18+
19+
20+
class ErrorHandler(object):
21+
"""
22+
This class is responsible for handling any HTTP errors that occur
23+
when a service operation is called. It is registered for the
24+
``after-call`` event and will have the opportunity to inspect
25+
all operation calls. If the HTTP response contains an error
26+
``status_code`` an appropriate error message will be printed and
27+
the handler will short-circuit all further processing by exiting
28+
with an appropriate error code.
29+
"""
30+
31+
ClientError = "A client error ({error_code}) occurred: {error_message}"
32+
ServerError = "A server error ({error_code}) occurred: {error_message}"
33+
34+
def __call__(self, http_response, parsed, operation, **kwargs):
35+
LOG.debug('HTTP Response Code: %d', http_response.status_code)
36+
if http_response.status_code >= 500:
37+
code, message = self._get_error_code_and_message(parsed)
38+
sys.stderr.write(self.ServerError.format(error_code=code,
39+
error_message=message))
40+
sys.stderr.write('\n')
41+
sys.exit(http_response.status_code - 399)
42+
if http_response.status_code >= 400 or http_response.status_code == 301:
43+
code, message = self._get_error_code_and_message(parsed)
44+
sys.stderr.write(self.ClientError.format(error_code=code,
45+
error_message=message))
46+
sys.stderr.write('\n')
47+
sys.exit(http_response.status_code - 399)
48+
return 0
49+
50+
def _get_error_code_and_message(self, response):
51+
code = 'Unknown'
52+
message = 'Unknown'
53+
if 'Errors' in response:
54+
if isinstance(response['Errors'], list):
55+
error = response['Errors'][-1]
56+
if 'Code' in error:
57+
code = error['Code']
58+
elif 'Type' in error:
59+
code = error['Type']
60+
if 'Message' in error:
61+
message = error['Message']
62+
return (code, message)

awscli/handlers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1818
"""
1919
from awscli.argprocess import ParamShorthand
20+
from awscli.errorhandler import ErrorHandler
2021
from awscli.customizations.streamingoutputarg import add_streaming_output_arg
2122
from awscli.customizations.addexamples import add_examples
2223
from awscli.customizations.removals import register_removals
@@ -33,9 +34,12 @@
3334
from awscli.customizations.sessendemail import register_ses_send_email
3435
from awscli.customizations.iamvirtmfa import IAMVMFAWrapper
3536

37+
3638
def awscli_initialize(event_handlers):
3739
param_shorthand = ParamShorthand()
3840
event_handlers.register('process-cli-arg', param_shorthand)
41+
error_handler = ErrorHandler()
42+
event_handlers.register('after-call.*.*', error_handler)
3943
# The following will get fired for every option we are
4044
# documenting. It will attempt to add an example_fn on to
4145
# the parameter object if the parameter supports shorthand

0 commit comments

Comments
 (0)