Commit 88c49fe3 authored by R. David Murray's avatar R. David Murray

#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.
parent f767f08e
...@@ -203,8 +203,8 @@ argument to :class:`ArgumentParser`. ...@@ -203,8 +203,8 @@ argument to :class:`ArgumentParser`.
add_help add_help
^^^^^^^^ ^^^^^^^^
By default, ArgumentParser objects add a ``-h/--help`` option which simply By default, ArgumentParser objects add an option which simply displays
displays the parser's help message. For example, consider a file named the parser's help message. For example, consider a file named
``myprogram.py`` containing the following code:: ``myprogram.py`` containing the following code::
import argparse import argparse
...@@ -234,12 +234,27 @@ This can be achieved by passing ``False`` as the ``add_help=`` argument to ...@@ -234,12 +234,27 @@ This can be achieved by passing ``False`` as the ``add_help=`` argument to
optional arguments: optional arguments:
--foo FOO foo help --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 prefix_chars
^^^^^^^^^^^^ ^^^^^^^^^^^^
Most command-line options will use ``'-'`` as the prefix, e.g. ``-f/--foo``. 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 like ``+f`` or ``/foo``, may specify them using the ``prefix_chars=`` argument
to the ArgumentParser constructor:: to the ArgumentParser constructor::
......
...@@ -1561,13 +1561,16 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): ...@@ -1561,13 +1561,16 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
# add help and version arguments if necessary # add help and version arguments if necessary
# (using explicit default to override global argument_default) # (using explicit default to override global argument_default)
default_prefix = '-' if '-' in prefix_chars else prefix_chars[0]
if self.add_help: if self.add_help:
self.add_argument( 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')) help=_('show this help message and exit'))
if self.version: if self.version:
self.add_argument( self.add_argument(
'-v', '--version', action='version', default=SUPPRESS, default_prefix+'v', default_prefix*2+'version',
action='version', default=SUPPRESS,
version=self.version, version=self.version,
help=_("show program's version number and exit")) help=_("show program's version number and exit"))
......
...@@ -417,7 +417,7 @@ class TestOptionalsSingleDoubleDash(ParserTestCase): ...@@ -417,7 +417,7 @@ class TestOptionalsSingleDoubleDash(ParserTestCase):
class TestOptionalsAlternatePrefixChars(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) parser_signature = Sig(prefix_chars='+:/', add_help=False)
argument_signatures = [ argument_signatures = [
...@@ -425,7 +425,7 @@ class TestOptionalsAlternatePrefixChars(ParserTestCase): ...@@ -425,7 +425,7 @@ class TestOptionalsAlternatePrefixChars(ParserTestCase):
Sig('::bar'), Sig('::bar'),
Sig('/baz', action='store_const', const=42), Sig('/baz', action='store_const', const=42),
] ]
failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz'] failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz', '-h', '--help', '+h', '::help', '/help']
successes = [ successes = [
('', NS(f=False, bar=None, baz=None)), ('', NS(f=False, bar=None, baz=None)),
('+f', NS(f=True, bar=None, baz=None)), ('+f', NS(f=True, bar=None, baz=None)),
...@@ -436,6 +436,27 @@ class TestOptionalsAlternatePrefixChars(ParserTestCase): ...@@ -436,6 +436,27 @@ class TestOptionalsAlternatePrefixChars(ParserTestCase):
] ]
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)),
('+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 TestOptionalsShortLong(ParserTestCase): class TestOptionalsShortLong(ParserTestCase):
"""Test a combination of single- and double-dash option strings""" """Test a combination of single- and double-dash option strings"""
...@@ -1655,12 +1676,18 @@ class TestAddSubparsers(TestCase): ...@@ -1655,12 +1676,18 @@ 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): def _get_parser(self, subparser_help=False, prefix_chars=None):
# create a parser with a subparsers argument # create a parser with a subparsers argument
parser = ErrorRaisingArgumentParser( if prefix_chars:
prog='PROG', description='main description') parser = ErrorRaisingArgumentParser(
parser.add_argument( prog='PROG', description='main description', prefix_chars=prefix_chars)
'--foo', action='store_true', help='foo help') 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( parser.add_argument(
'bar', type=float, help='bar help') 'bar', type=float, help='bar help')
...@@ -1739,6 +1766,44 @@ class TestAddSubparsers(TestCase): ...@@ -1739,6 +1766,44 @@ class TestAddSubparsers(TestCase):
--foo foo help --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): def test_parser_command_help(self):
self.assertEqual(self.command_help_parser.format_usage(), self.assertEqual(self.command_help_parser.format_usage(),
'usage: PROG [-h] [--foo] bar {1,2} ...\n') 'usage: PROG [-h] [--foo] bar {1,2} ...\n')
......
...@@ -802,6 +802,7 @@ John Tromp ...@@ -802,6 +802,7 @@ John Tromp
Jason Trowbridge Jason Trowbridge
Anthony Tuininga Anthony Tuininga
Stephen Turner Stephen Turner
Theodore Turocy
Bill Tutt Bill Tutt
Doobee R. Tzeck Doobee R. Tzeck
Eren Türkay Eren Türkay
......
...@@ -35,6 +35,10 @@ Extensions ...@@ -35,6 +35,10 @@ Extensions
Library 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 #7372: Fix pstats regression when stripping paths from profile - Issue #7372: Fix pstats regression when stripping paths from profile
data generated with the profile module. data generated with the profile module.
......
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