Avoid "ambiguous option" when only current/deprecated forms match

argparse.ArgumentParser allows to specify partially options:

  nova boot ... --key-nam mykey
  # is equivalent to
  nova boot ... --key-name mykey

an error is raised if the provided prefix matchs 0 or 2+ options:

  nova boot ... --os value
  # raises an "ambiguous option" error because --os could match
  # --os-username, --os_username ...

even if the provided prefix matchs only the current/deprecated
forms of the same attribute:

  nova boot ... --key mykey
  # raises an "ambiguous option" error because --key could match
  # --my-key, --my_key ...

This change extends argparse.ArgumentParser to avoid raising an
"ambiguous option" when the provided prefix matchs only the current and
deprecated forms of the same attribute.

Change-Id: I1089901de769df3312d4a15b6d6e5e60b1ed51e0
This commit is contained in:
Cedric Brandily 2014-10-13 11:19:21 +02:00
parent e82b46bb93
commit f4709f02c2
2 changed files with 42 additions and 0 deletions
novaclient

@ -229,6 +229,23 @@ class NovaClientArgumentParser(argparse.ArgumentParser):
'mainp': progparts[0],
'subp': progparts[2]})
def _get_option_tuples(self, option_string):
"""returns (action, option, value) candidates for an option prefix
Returns [first candidate] if all candidates refers to current and
deprecated forms of the same options: "nova boot ... --key KEY"
parsing succeed because --key could only match --key-name,
--key_name which are current/deprecated forms of the same option.
"""
option_tuples = (super(NovaClientArgumentParser, self)
._get_option_tuples(option_string))
if len(option_tuples) > 1:
normalizeds = [option.replace('_', '-')
for action, option, value in option_tuples]
if len(set(normalizeds)) == 1:
return option_tuples[:1]
return option_tuples
class OpenStackComputeShell(object):

@ -37,6 +37,31 @@ FAKE_ENV2 = {'OS_USER_ID': 'user_id',
'OS_AUTH_URL': 'http://no.where'}
class ParserTest(utils.TestCase):
def setUp(self):
super(ParserTest, self).setUp()
self.parser = novaclient.shell.NovaClientArgumentParser()
def test_ambiguous_option(self):
self.parser.add_argument('--tic')
self.parser.add_argument('--tac')
try:
self.parser.parse_args(['--t'])
except SystemExit as err:
self.assertEqual(2, err.code)
else:
self.fail('SystemExit not raised')
def test_not_really_ambiguous_option(self):
# current/deprecated forms of the same option
self.parser.add_argument('--tic-tac', action="store_true")
self.parser.add_argument('--tic_tac', action="store_true")
args = self.parser.parse_args(['--tic'])
self.assertTrue(args.tic_tac)
class ShellTest(utils.TestCase):
def make_env(self, exclude=None, fake_env=FAKE_ENV):