Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unicode argument processing for py2 #679

Merged
merged 4 commits into from
Mar 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ CHANGELOG
Next Release (TBD)
==================

* bugfix:``CLI Arguments``: Fix issue where unicode command line
arguments were not being handled correctly
(`issue 679 <https://github.com/aws/aws-cli/pull/679>`__)


1.3.0
=====

* bugfix:``aws s3``: Fix issue where S3 downloads would hang
in certain cases and could not be interrupted
(`issue 650 <https://github.com/aws/aws-cli/issues/650>`__,
Expand Down
15 changes: 15 additions & 0 deletions awscli/argparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import argparse
import sys
import six
from difflib import get_close_matches


Expand Down Expand Up @@ -43,6 +45,19 @@ def _check_value(self, action, value):
msg.extend(extra)
raise argparse.ArgumentError(action, '\n'.join(msg))

def parse_known_args(self, args, namespace=None):
parsed, remaining = super(CLIArgParser, self).parse_known_args(args, namespace)
terminal_encoding = getattr(sys.stdin, 'encoding', 'utf-8')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really want a default value of utf-8 here? I guess I'm not sure what it would mean for sys.stdin not to have an encoding attribute.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've only encountered it when something patches out sys.stdin, and is file-like enough to work, but is missing attributes such as encoding. For example, our test runner will do something like:

original = sys.stdin
sys.stdin = cStringIO.StringIO()
try:
  ...
finally:
  sys.stdin = original

if terminal_encoding is None:
# In some cases, sys.stdin won't have an encoding set,
# (e.g if it's set to a StringIO). In this case we just
# default to utf-8.
terminal_encoding = 'utf-8'
for arg, value in vars(parsed).items():
if isinstance(value, six.binary_type):
setattr(parsed, arg, value.decode(terminal_encoding))
return parsed, remaining


class MainArgParser(CLIArgParser):
Formatter = argparse.RawTextHelpFormatter
Expand Down
13 changes: 7 additions & 6 deletions awscli/argprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def _check_for_uri_param(param, value):
try:
return get_paramfile(value)
except ResourceLoadingError as e:
raise ParamError(param, str(e))
raise ParamError(param, six.text_type(e))


def detect_shape_structure(param):
Expand Down Expand Up @@ -163,7 +163,8 @@ def get_parse_method_for_param(self, param, value=None):
check_val = value[0]
else:
check_val = value.strip()
if isinstance(check_val, str) and check_val.startswith(('[', '{')):
if isinstance(check_val, six.string_types) and check_val.startswith(
('[', '{')):
LOG.debug("Param %s looks like JSON, not considered for "
"param shorthand.", param.py_name)
return
Expand Down Expand Up @@ -345,7 +346,7 @@ def _split_on_commas(self, value):
try:
return utils.split_on_commas(value)
except ValueError as e:
raise ParamSyntaxError(str(e))
raise ParamSyntaxError(six.text_type(e))


def unpack_cli_arg(parameter, value):
Expand All @@ -370,7 +371,7 @@ def unpack_cli_arg(parameter, value):
elif parameter.type in COMPLEX_TYPES:
return unpack_complex_cli_arg(parameter, value)
else:
return str(value)
return six.text_type(value)


def unpack_complex_cli_arg(parameter, value):
Expand Down Expand Up @@ -415,8 +416,8 @@ def unpack_scalar_cli_arg(parameter, value):
raise ParamError(parameter, msg)
return open(file_path, 'rb')
elif parameter.type == 'boolean':
if isinstance(value, str) and value.lower() == 'false':
if isinstance(value, six.string_types) and value.lower() == 'false':
return False
return bool(value)
else:
return str(value)
return six.text_type(value)
45 changes: 45 additions & 0 deletions tests/unit/elasticbeanstalk/test_create_application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python
# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
from tests.unit import BaseAWSCommandParamsTest, unittest
import sys
import six


class TestUpdateConfigurationTemplate(BaseAWSCommandParamsTest):

prefix = 'elasticbeanstalk create-application'

def test_ascii(self):
cmdline = self.prefix
cmdline += ' --application-name FooBar'
result = {'ApplicationName': 'FooBar',}
self.assert_params_for_cmd(cmdline, result)

@unittest.skipIf(
six.PY3, 'Unicode cmd line test only is relevant to python2.')
def test_py2_bytestring_unicode(self):
# In Python2, sys.argv is a list of bytestrings that are encoded
# in whatever encoding the terminal uses. We have an extra step
# where we need to decode the bytestring into unicode. In
# python3, sys.argv is a list of unicode objects so this test
# doesn't make sense for python3.
cmdline = self.prefix
app_name = u'\u2713'
cmdline += u' --application-name %s' % app_name
encoding = getattr(sys.stdin, 'encoding')
if encoding is None:
encoding = 'utf-8'
cmdline = cmdline.encode(encoding)
result = {'ApplicationName': u'\u2713',}
self.assert_params_for_cmd(cmdline, result)