mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-138525: Support single-dash long options and prefix_chars in BooleanOptionalAction (GH-138692)
-nofoo is generated for -foo. ++no-foo is generated for ++foo. /nofoo is generated for /foo.
This commit is contained in:
parent
cde19e565c
commit
425fd85ca3
5 changed files with 102 additions and 4 deletions
|
|
@ -1445,8 +1445,18 @@ this API may be passed as the ``action`` parameter to
|
|||
>>> parser.parse_args(['--no-foo'])
|
||||
Namespace(foo=False)
|
||||
|
||||
Single-dash long options are also supported.
|
||||
For example, negative option ``-nofoo`` is automatically added for
|
||||
positive option ``-foo``.
|
||||
But no additional options are added for short options such as ``-f``.
|
||||
|
||||
.. versionadded:: 3.9
|
||||
|
||||
.. versionchanged:: next
|
||||
Added support for single-dash options.
|
||||
|
||||
Added support for alternate prefix_chars_.
|
||||
|
||||
|
||||
The parse_args() method
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -416,6 +416,10 @@ Improved modules
|
|||
argparse
|
||||
--------
|
||||
|
||||
* The :class:`~argparse.BooleanOptionalAction` action supports now single-dash
|
||||
long options and alternate prefix characters.
|
||||
(Contributed by Serhiy Storchaka in :gh:`138525`.)
|
||||
|
||||
* Changed the *suggest_on_error* parameter of :class:`argparse.ArgumentParser` to
|
||||
default to ``True``. This enables suggestions for mistyped arguments by default.
|
||||
(Contributed by Jakob Schluse in :gh:`140450`.)
|
||||
|
|
|
|||
|
|
@ -932,15 +932,26 @@ def __init__(self,
|
|||
deprecated=False):
|
||||
|
||||
_option_strings = []
|
||||
neg_option_strings = []
|
||||
for option_string in option_strings:
|
||||
_option_strings.append(option_string)
|
||||
|
||||
if option_string.startswith('--'):
|
||||
if option_string.startswith('--no-'):
|
||||
if len(option_string) > 2 and option_string[0] == option_string[1]:
|
||||
# two-dash long option: '--foo' -> '--no-foo'
|
||||
if option_string.startswith('no-', 2):
|
||||
raise ValueError(f'invalid option name {option_string!r} '
|
||||
f'for BooleanOptionalAction')
|
||||
option_string = '--no-' + option_string[2:]
|
||||
option_string = option_string[:2] + 'no-' + option_string[2:]
|
||||
_option_strings.append(option_string)
|
||||
neg_option_strings.append(option_string)
|
||||
elif len(option_string) > 2 and option_string[0] != option_string[1]:
|
||||
# single-dash long option: '-foo' -> '-nofoo'
|
||||
if option_string.startswith('no', 1):
|
||||
raise ValueError(f'invalid option name {option_string!r} '
|
||||
f'for BooleanOptionalAction')
|
||||
option_string = option_string[:1] + 'no' + option_string[1:]
|
||||
_option_strings.append(option_string)
|
||||
neg_option_strings.append(option_string)
|
||||
|
||||
super().__init__(
|
||||
option_strings=_option_strings,
|
||||
|
|
@ -950,11 +961,12 @@ def __init__(self,
|
|||
required=required,
|
||||
help=help,
|
||||
deprecated=deprecated)
|
||||
self.neg_option_strings = neg_option_strings
|
||||
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
if option_string in self.option_strings:
|
||||
setattr(namespace, self.dest, not option_string.startswith('--no-'))
|
||||
setattr(namespace, self.dest, option_string not in self.neg_option_strings)
|
||||
|
||||
def format_usage(self):
|
||||
return ' | '.join(self.option_strings)
|
||||
|
|
|
|||
|
|
@ -805,6 +805,76 @@ def test_invalid_name(self):
|
|||
self.assertEqual(str(cm.exception),
|
||||
"invalid option name '--no-foo' for BooleanOptionalAction")
|
||||
|
||||
class TestBooleanOptionalActionSingleDash(ParserTestCase):
|
||||
"""Tests BooleanOptionalAction with single dash"""
|
||||
|
||||
argument_signatures = [
|
||||
Sig('-foo', '-x', action=argparse.BooleanOptionalAction),
|
||||
]
|
||||
failures = ['--foo', '--no-foo', '-no-foo', '-no-x', '-nox']
|
||||
successes = [
|
||||
('', NS(foo=None)),
|
||||
('-foo', NS(foo=True)),
|
||||
('-nofoo', NS(foo=False)),
|
||||
('-x', NS(foo=True)),
|
||||
]
|
||||
|
||||
def test_invalid_name(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
parser.add_argument('-nofoo', action=argparse.BooleanOptionalAction)
|
||||
self.assertEqual(str(cm.exception),
|
||||
"invalid option name '-nofoo' for BooleanOptionalAction")
|
||||
|
||||
class TestBooleanOptionalActionAlternatePrefixChars(ParserTestCase):
|
||||
"""Tests BooleanOptionalAction with custom prefixes"""
|
||||
|
||||
parser_signature = Sig(prefix_chars='+-', add_help=False)
|
||||
argument_signatures = [Sig('++foo', action=argparse.BooleanOptionalAction)]
|
||||
failures = ['--foo', '--no-foo']
|
||||
successes = [
|
||||
('', NS(foo=None)),
|
||||
('++foo', NS(foo=True)),
|
||||
('++no-foo', NS(foo=False)),
|
||||
]
|
||||
|
||||
def test_invalid_name(self):
|
||||
parser = argparse.ArgumentParser(prefix_chars='+/')
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
'BooleanOptionalAction.*is not valid for positional arguments'):
|
||||
parser.add_argument('--foo', action=argparse.BooleanOptionalAction)
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
parser.add_argument('++no-foo', action=argparse.BooleanOptionalAction)
|
||||
self.assertEqual(str(cm.exception),
|
||||
"invalid option name '++no-foo' for BooleanOptionalAction")
|
||||
|
||||
class TestBooleanOptionalActionSingleAlternatePrefixChar(ParserTestCase):
|
||||
"""Tests BooleanOptionalAction with single alternate prefix char"""
|
||||
|
||||
parser_signature = Sig(prefix_chars='+/', add_help=False)
|
||||
argument_signatures = [
|
||||
Sig('+foo', '+x', action=argparse.BooleanOptionalAction),
|
||||
]
|
||||
failures = ['++foo', '++no-foo', '++nofoo',
|
||||
'-no-foo', '-nofoo', '+no-foo', '-nofoo',
|
||||
'+no-x', '+nox', '-no-x', '-nox']
|
||||
successes = [
|
||||
('', NS(foo=None)),
|
||||
('+foo', NS(foo=True)),
|
||||
('+nofoo', NS(foo=False)),
|
||||
('+x', NS(foo=True)),
|
||||
]
|
||||
|
||||
def test_invalid_name(self):
|
||||
parser = argparse.ArgumentParser(prefix_chars='+/')
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
'BooleanOptionalAction.*is not valid for positional arguments'):
|
||||
parser.add_argument('-foo', action=argparse.BooleanOptionalAction)
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
parser.add_argument('+nofoo', action=argparse.BooleanOptionalAction)
|
||||
self.assertEqual(str(cm.exception),
|
||||
"invalid option name '+nofoo' for BooleanOptionalAction")
|
||||
|
||||
class TestBooleanOptionalActionRequired(ParserTestCase):
|
||||
"""Tests BooleanOptionalAction required"""
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Add support for single-dash long options and alternate prefix characters in
|
||||
:class:`argparse.BooleanOptionalAction`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue