From 81039a1e36dd8d85b14f0753bf6d4de14eaeeb94 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Wed, 7 Dec 2016 15:39:22 -0600 Subject: [PATCH] Handle formatting of subcommand name in error output On Python 2, decoding all arguments leads to the possibility that users that use the wrong command or mistype the name will see error output with a unicode string's representation instead of one without it. To avoid this we try and find the first non-option string in the argument list and replace it with an string that is not text only on Python 2. If we encoded the string at all times, then users installing glanceclient on Python 3 would see b'invalid-subcommand' instead. That's as bad as seeing u'invalid-subcommand' on Python 2. Closes-bug: 1533090 Change-Id: I018769e159a607ebb233902cbeb13b95ca417190 --- glanceclient/shell.py | 36 ++++++++++++++++++++++++++- glanceclient/tests/unit/test_shell.py | 29 +++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/glanceclient/shell.py b/glanceclient/shell.py index 51e02a67..b3af1853 100644 --- a/glanceclient/shell.py +++ b/glanceclient/shell.py @@ -31,6 +31,7 @@ import traceback from oslo_utils import encodeutils from oslo_utils import importutils +import six import six.moves.urllib.parse as urlparse import glanceclient @@ -539,7 +540,13 @@ class OpenStackImagesShell(object): self.do_help(options, parser=parser) return 0 - # Short-circuit and deal with help command right away. + # NOTE(sigmavirus24): Above, args is defined as the left over + # arguments from parser.parse_known_args(). This allows us to + # skip any parameters to command-line flags that may have been passed + # to glanceclient, e.g., --os-auth-token. + self._fixup_subcommand(args, argv) + + # short-circuit and deal with help command right away. sub_parser = _get_subparser(api_version) args = sub_parser.parse_args(argv) @@ -609,6 +616,33 @@ class OpenStackImagesShell(object): print("To display trace use next command:\n" "osprofiler trace show --html %s " % trace_id) + @staticmethod + def _fixup_subcommand(unknown_args, argv): + # NOTE(sigmavirus24): Sometimes users pass the wrong subcommand name + # to glanceclient. If they're using Python 2 they will see an error: + # > invalid choice: u'imgae-list' (choose from ...) + # To avoid this, we look at the extra args already parsed from above + # and try to predict what the subcommand will be based on it being the + # first non - or -- prefixed argument in args. We then find that in + # argv and encode it from unicode so users don't see the pesky `u'` + # prefix. + for arg in unknown_args: + if not arg.startswith('-'): # This will cover both - and -- + subcommand_name = arg + break + else: + subcommand_name = '' + + if (subcommand_name and six.PY2 and + isinstance(subcommand_name, six.text_type)): + # NOTE(sigmavirus24): if we found a subcommand name, then let's + # find it in the argv list and replace it with a bytes object + # instead. Note, that if we encode the argument on Python 3, the + # user will instead see a pesky `b'` string instead of the `u'` + # string we mention above. + subcommand_index = argv.index(subcommand_name) + argv[subcommand_index] = encodeutils.safe_encode(subcommand_name) + @utils.arg('command', metavar='', nargs='?', help='Display help for .') def do_help(self, args, parser): diff --git a/glanceclient/tests/unit/test_shell.py b/glanceclient/tests/unit/test_shell.py index d175852b..5e59be1b 100644 --- a/glanceclient/tests/unit/test_shell.py +++ b/glanceclient/tests/unit/test_shell.py @@ -163,6 +163,35 @@ class ShellTest(testutils.TestCase): sys.stderr = orig_stderr return (stdout, stderr) + def test_fixup_subcommand(self): + arglist = [u'image-list', u'--help'] + if six.PY2: + expected_arglist = [b'image-list', u'--help'] + elif six.PY3: + expected_arglist = [u'image-list', u'--help'] + + openstack_shell.OpenStackImagesShell._fixup_subcommand( + arglist, arglist + ) + self.assertEqual(expected_arglist, arglist) + + def test_fixup_subcommand_with_options_preceding(self): + arglist = [u'--os-auth-token', u'abcdef', u'image-list', u'--help'] + unknown = arglist[2:] + if six.PY2: + expected_arglist = [ + u'--os-auth-token', u'abcdef', b'image-list', u'--help' + ] + elif six.PY3: + expected_arglist = [ + u'--os-auth-token', u'abcdef', u'image-list', u'--help' + ] + + openstack_shell.OpenStackImagesShell._fixup_subcommand( + unknown, arglist + ) + self.assertEqual(expected_arglist, arglist) + def test_help_unknown_command(self): shell = openstack_shell.OpenStackImagesShell() argstr = '--os-image-api-version 2 help foofoo'