Commit fd311a71 authored by Steven Bethard's avatar Steven Bethard

Add subparser aliases for argparse. Resolves issue 9324. Approved by Georg for...

Add subparser aliases for argparse. Resolves issue 9324. Approved by Georg for beta2 on the tracker.
parent 04129748
...@@ -1428,6 +1428,16 @@ Sub-commands ...@@ -1428,6 +1428,16 @@ Sub-commands
{foo,bar} additional help {foo,bar} additional help
Furthermore, ``add_parser`` supports an additional ``aliases`` argument,
which allows multiple strings to refer to the same subparser. This example,
like ``svn``, aliases ``co`` as a shorthand for ``checkout``::
>>> parser = argparse.ArgumentParser()
>>> subparsers = parser.add_subparsers()
>>> checkout = subparsers.add_parser('checkout', aliases=['co'])
>>> checkout.add_argument('foo')
>>> parser.parse_args(['co', 'bar'])
Namespace(foo='bar')
One particularly effective way of handling sub-commands is to combine the use One particularly effective way of handling sub-commands is to combine the use
of the :meth:`add_subparsers` method with calls to :meth:`set_defaults` so of the :meth:`add_subparsers` method with calls to :meth:`set_defaults` so
...@@ -1466,7 +1476,7 @@ Sub-commands ...@@ -1466,7 +1476,7 @@ Sub-commands
>>> args.func(args) >>> args.func(args)
((XYZYX)) ((XYZYX))
This way, you can let :meth:`parse_args` does the job of calling the This way, you can let :meth:`parse_args` do the job of calling the
appropriate function after argument parsing is complete. Associating appropriate function after argument parsing is complete. Associating
functions with actions like this is typically the easiest way to handle the functions with actions like this is typically the easiest way to handle the
different actions for each of your subparsers. However, if it is necessary different actions for each of your subparsers. However, if it is necessary
......
...@@ -1023,9 +1023,13 @@ class _SubParsersAction(Action): ...@@ -1023,9 +1023,13 @@ class _SubParsersAction(Action):
class _ChoicesPseudoAction(Action): class _ChoicesPseudoAction(Action):
def __init__(self, name, help): def __init__(self, name, aliases, help):
metavar = dest = name
if aliases:
metavar += ' (%s)' % ', '.join(aliases)
sup = super(_SubParsersAction._ChoicesPseudoAction, self) sup = super(_SubParsersAction._ChoicesPseudoAction, self)
sup.__init__(option_strings=[], dest=name, help=help) sup.__init__(option_strings=[], dest=dest, help=help,
metavar=metavar)
def __init__(self, def __init__(self,
option_strings, option_strings,
...@@ -1053,15 +1057,22 @@ class _SubParsersAction(Action): ...@@ -1053,15 +1057,22 @@ class _SubParsersAction(Action):
if kwargs.get('prog') is None: if kwargs.get('prog') is None:
kwargs['prog'] = '%s %s' % (self._prog_prefix, name) kwargs['prog'] = '%s %s' % (self._prog_prefix, name)
aliases = kwargs.pop('aliases', ())
# create a pseudo-action to hold the choice help # create a pseudo-action to hold the choice help
if 'help' in kwargs: if 'help' in kwargs:
help = kwargs.pop('help') help = kwargs.pop('help')
choice_action = self._ChoicesPseudoAction(name, help) choice_action = self._ChoicesPseudoAction(name, aliases, help)
self._choices_actions.append(choice_action) self._choices_actions.append(choice_action)
# create the parser and add it to the map # create the parser and add it to the map
parser = self._parser_class(**kwargs) parser = self._parser_class(**kwargs)
self._name_parser_map[name] = parser self._name_parser_map[name] = parser
# make parser available under aliases also
for alias in aliases:
self._name_parser_map[alias] = parser
return parser return parser
def _get_subactions(self): def _get_subactions(self):
......
...@@ -1708,7 +1708,8 @@ class TestAddSubparsers(TestCase): ...@@ -1708,7 +1708,8 @@ class TestAddSubparsers(TestCase):
def assertArgumentParserError(self, *args, **kwargs): def assertArgumentParserError(self, *args, **kwargs):
self.assertRaises(ArgumentParserError, *args, **kwargs) self.assertRaises(ArgumentParserError, *args, **kwargs)
def _get_parser(self, subparser_help=False, prefix_chars=None): def _get_parser(self, subparser_help=False, prefix_chars=None,
aliases=False):
# create a parser with a subparsers argument # create a parser with a subparsers argument
if prefix_chars: if prefix_chars:
parser = ErrorRaisingArgumentParser( parser = ErrorRaisingArgumentParser(
...@@ -1724,13 +1725,21 @@ class TestAddSubparsers(TestCase): ...@@ -1724,13 +1725,21 @@ class TestAddSubparsers(TestCase):
'bar', type=float, help='bar help') 'bar', type=float, help='bar help')
# check that only one subparsers argument can be added # check that only one subparsers argument can be added
subparsers = parser.add_subparsers(help='command help') subparsers_kwargs = {}
if aliases:
subparsers_kwargs['metavar'] = 'COMMAND'
subparsers_kwargs['title'] = 'commands'
else:
subparsers_kwargs['help'] = 'command help'
subparsers = parser.add_subparsers(**subparsers_kwargs)
self.assertArgumentParserError(parser.add_subparsers) self.assertArgumentParserError(parser.add_subparsers)
# add first sub-parser # add first sub-parser
parser1_kwargs = dict(description='1 description') parser1_kwargs = dict(description='1 description')
if subparser_help: if subparser_help:
parser1_kwargs['help'] = '1 help' parser1_kwargs['help'] = '1 help'
if aliases:
parser1_kwargs['aliases'] = ['1alias1', '1alias2']
parser1 = subparsers.add_parser('1', **parser1_kwargs) parser1 = subparsers.add_parser('1', **parser1_kwargs)
parser1.add_argument('-w', type=int, help='w help') parser1.add_argument('-w', type=int, help='w help')
parser1.add_argument('x', choices='abc', help='x help') parser1.add_argument('x', choices='abc', help='x help')
...@@ -1947,6 +1956,44 @@ class TestAddSubparsers(TestCase): ...@@ -1947,6 +1956,44 @@ class TestAddSubparsers(TestCase):
-y {1,2,3} y help -y {1,2,3} y help
''')) '''))
def test_alias_invocation(self):
parser = self._get_parser(aliases=True)
self.assertEqual(
parser.parse_known_args('0.5 1alias1 b'.split()),
(NS(foo=False, bar=0.5, w=None, x='b'), []),
)
self.assertEqual(
parser.parse_known_args('0.5 1alias2 b'.split()),
(NS(foo=False, bar=0.5, w=None, x='b'), []),
)
def test_error_alias_invocation(self):
parser = self._get_parser(aliases=True)
self.assertArgumentParserError(parser.parse_args,
'0.5 1alias3 b'.split())
def test_alias_help(self):
parser = self._get_parser(aliases=True, subparser_help=True)
self.maxDiff = None
self.assertEqual(parser.format_help(), textwrap.dedent("""\
usage: PROG [-h] [--foo] bar COMMAND ...
main description
positional arguments:
bar bar help
optional arguments:
-h, --help show this help message and exit
--foo foo help
commands:
COMMAND
1 (1alias1, 1alias2)
1 help
2 2 help
"""))
# ============ # ============
# Groups tests # Groups tests
# ============ # ============
......
...@@ -734,6 +734,7 @@ Hajime Saitou ...@@ -734,6 +734,7 @@ Hajime Saitou
George Sakkis George Sakkis
Rich Salz Rich Salz
Kevin Samborn Kevin Samborn
Adrian Sampson
Ilya Sandler Ilya Sandler
Mark Sapiro Mark Sapiro
Ty Sarna Ty Sarna
......
...@@ -84,6 +84,8 @@ Build ...@@ -84,6 +84,8 @@ Build
- The Windows build now uses Tcl/Tk 8.5.9 and sqlite3 3.7.4. - The Windows build now uses Tcl/Tk 8.5.9 and sqlite3 3.7.4.
- Issue #9234: argparse supports alias names for subparsers.
What's New in Python 3.2 Beta 1? What's New in Python 3.2 Beta 1?
================================ ================================
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment