From 1cbf78e040b1ddfe7a1520d21ea34e1098848713 Mon Sep 17 00:00:00 2001 From: "R. David Murray" Date: Tue, 3 Aug 2010 18:14:01 +0000 Subject: [PATCH] Merged revisions 83675 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r83675 | r.david.murray | 2010-08-03 13:56:09 -0400 (Tue, 03 Aug 2010) | 12 lines #9444: use first of prefix_chars for help opt instead of raising error An argparse option parser created with a prefix_chars that did not include a '-' would happily add -h and --help options, and then throw an error when it tried to format the help because the - was an invalid prefix character. This patch makes it use the first character of prefix_chars as the character for the help options if and only if '-' is not one of the valid prefix_chars. Fix by Theodore Turocy, unit tests by Catherine Devlin. ........ --- Doc/library/argparse.rst | 21 ++++++++-- Lib/argparse.py | 7 +++- Lib/test/test_argparse.py | 81 +++++++++++++++++++++++++++++++++++---- Misc/ACKS | 1 + Misc/NEWS | 4 ++ 5 files changed, 101 insertions(+), 13 deletions(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index b3a1171048b..45eddca644f 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -203,8 +203,8 @@ argument to :class:`ArgumentParser`. add_help ^^^^^^^^ -By default, ArgumentParser objects add a ``-h/--help`` option which simply -displays the parser's help message. For example, consider a file named +By default, ArgumentParser objects add an option which simply displays +the parser's help message. For example, consider a file named ``myprogram.py`` containing the following code:: import argparse @@ -234,12 +234,27 @@ This can be achieved by passing ``False`` as the ``add_help=`` argument to optional arguments: --foo FOO foo help +The help option is typically ``-h/--help``. The exception to this is +if the ``prefix_chars=`` is specified and does not include ``'-'``, in +which case ``-h`` and ``--help`` are not valid options. In +this case, the first character in ``prefix_chars`` is used to prefix +the help options:: + + >>> parser = argparse.ArgumentParser(prog='PROG', prefix_chars='+/') + >>> parser.print_help() + usage: PROG [+h] + + optional arguments: + +h, ++help show this help message and exit + + prefix_chars ^^^^^^^^^^^^ Most command-line options will use ``'-'`` as the prefix, e.g. ``-f/--foo``. -Parsers that need to support additional prefix characters, e.g. for options +Parsers that need to support different or additional prefix +characters, e.g. for options like ``+f`` or ``/foo``, may specify them using the ``prefix_chars=`` argument to the ArgumentParser constructor:: diff --git a/Lib/argparse.py b/Lib/argparse.py index f9279aa5446..654ac487614 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1563,13 +1563,16 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): # add help and version arguments if necessary # (using explicit default to override global argument_default) + default_prefix = '-' if '-' in prefix_chars else prefix_chars[0] if self.add_help: self.add_argument( - '-h', '--help', action='help', default=SUPPRESS, + default_prefix+'h', default_prefix*2+'help', + action='help', default=SUPPRESS, help=_('show this help message and exit')) if self.version: self.add_argument( - '-v', '--version', action='version', default=SUPPRESS, + default_prefix+'v', default_prefix*2+'version', + action='version', default=SUPPRESS, version=self.version, help=_("show program's version number and exit")) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 30e53abf19d..3e1bf252050 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -420,7 +420,7 @@ class TestOptionalsSingleDoubleDash(ParserTestCase): class TestOptionalsAlternatePrefixChars(ParserTestCase): - """Test an Optional with a double-dash option string""" + """Test an Optional with option strings with custom prefixes""" parser_signature = Sig(prefix_chars='+:/', add_help=False) argument_signatures = [ @@ -428,6 +428,28 @@ class TestOptionalsAlternatePrefixChars(ParserTestCase): Sig('::bar'), Sig('/baz', action='store_const', const=42), ] + failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz', '-h', '--help', '+h', '::help', '/help'] + successes = [ + ('', NS(f=False, bar=None, baz=None)), + ('+f', NS(f=True, bar=None, baz=None)), + ('::ba B', NS(f=False, bar='B', baz=None)), + ('+f ::bar B', NS(f=True, bar='B', baz=None)), + ('+f /b', NS(f=True, bar=None, baz=42)), + ('/ba +f', NS(f=True, bar=None, baz=42)), + ] + + +class TestOptionalsAlternatePrefixCharsAddedHelp(ParserTestCase): + """When ``-`` not in prefix_chars, default operators created for help + should use the prefix_chars in use rather than - or -- + http://bugs.python.org/issue9444""" + + parser_signature = Sig(prefix_chars='+:/', add_help=True) + argument_signatures = [ + Sig('+f', action='store_true'), + Sig('::bar'), + Sig('/baz', action='store_const', const=42), + ] failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz'] successes = [ ('', NS(f=False, bar=None, baz=None)), @@ -435,10 +457,9 @@ class TestOptionalsAlternatePrefixChars(ParserTestCase): ('::ba B', NS(f=False, bar='B', baz=None)), ('+f ::bar B', NS(f=True, bar='B', baz=None)), ('+f /b', NS(f=True, bar=None, baz=42)), - ('/ba +f', NS(f=True, bar=None, baz=42)), + ('/ba +f', NS(f=True, bar=None, baz=42)) ] - class TestOptionalsShortLong(ParserTestCase): """Test a combination of single- and double-dash option strings""" @@ -1666,12 +1687,18 @@ class TestAddSubparsers(TestCase): def assertArgumentParserError(self, *args, **kwargs): self.assertRaises(ArgumentParserError, *args, **kwargs) - def _get_parser(self, subparser_help=False): + def _get_parser(self, subparser_help=False, prefix_chars=None): # create a parser with a subparsers argument - parser = ErrorRaisingArgumentParser( - prog='PROG', description='main description') - parser.add_argument( - '--foo', action='store_true', help='foo help') + if prefix_chars: + parser = ErrorRaisingArgumentParser( + prog='PROG', description='main description', prefix_chars=prefix_chars) + parser.add_argument( + prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help') + else: + parser = ErrorRaisingArgumentParser( + prog='PROG', description='main description') + parser.add_argument( + '--foo', action='store_true', help='foo help') parser.add_argument( 'bar', type=float, help='bar help') @@ -1750,6 +1777,44 @@ class TestAddSubparsers(TestCase): --foo foo help ''')) + def test_help_extra_prefix_chars(self): + # Make sure - is still used for help if it is a non-first prefix char + parser = self._get_parser(prefix_chars='+:-') + self.assertEqual(parser.format_usage(), + 'usage: PROG [-h] [++foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [-h] [++foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + -h, --help show this help message and exit + ++foo foo help + ''')) + + + def test_help_alternate_prefix_chars(self): + parser = self._get_parser(prefix_chars='+:/') + self.assertEqual(parser.format_usage(), + 'usage: PROG [+h] [++foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [+h] [++foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + +h, ++help show this help message and exit + ++foo foo help + ''')) + def test_parser_command_help(self): self.assertEqual(self.command_help_parser.format_usage(), 'usage: PROG [-h] [--foo] bar {1,2} ...\n') diff --git a/Misc/ACKS b/Misc/ACKS index 15436f48ab9..197a5022caa 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -782,6 +782,7 @@ John Tromp Jason Trowbridge Anthony Tuininga Stephen Turner +Theodore Turocy Bill Tutt Doobee R. Tzeck Eren Türkay diff --git a/Misc/NEWS b/Misc/NEWS index 323b0a32188..431c3d5c4c6 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -24,6 +24,10 @@ Core and Builtins Library ------- +- Issue #9444: Argparse now uses the first element of prefix_chars as + the option character for the added 'h/help' option if prefix_chars + does not contain a '-', instead of raising an error. + - Issue #9354: Provide getsockopt() in asyncore's file_wrapper. - Issue #7781: Fix restricting stats by entry counts in the pstats