summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/argparse.rst19
-rw-r--r--Doc/whatsnew/3.14.rst8
-rw-r--r--Lib/argparse.py20
-rw-r--r--Lib/test/test_argparse.py83
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2024-11-23-04-54-42.gh-issue-127133.WMoJjF.rst6
5 files changed, 41 insertions, 95 deletions
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 410b6e1..da4071d 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -1926,11 +1926,10 @@ Argument groups
Note that any arguments not in your user-defined groups will end up back
in the usual "positional arguments" and "optional arguments" sections.
- .. versionchanged:: 3.11
- Calling :meth:`add_argument_group` on an argument group is deprecated.
- This feature was never supported and does not always work correctly.
- The function exists on the API by accident through inheritance and
- will be removed in the future.
+ .. deprecated-removed:: 3.11 3.14
+ Calling :meth:`add_argument_group` on an argument group now raises an
+ exception. This nesting was never supported, often failed to work
+ correctly, and was unintentionally exposed through inheritance.
.. deprecated:: 3.14
Passing prefix_chars_ to :meth:`add_argument_group`
@@ -1993,11 +1992,11 @@ Mutual exclusion
--foo FOO foo help
--bar BAR bar help
- .. versionchanged:: 3.11
- Calling :meth:`add_argument_group` or :meth:`add_mutually_exclusive_group`
- on a mutually exclusive group is deprecated. These features were never
- supported and do not always work correctly. The functions exist on the
- API by accident through inheritance and will be removed in the future.
+ .. deprecated-removed:: 3.11 3.14
+ Calling :meth:`add_argument_group` or :meth:`add_mutually_exclusive_group`
+ on a mutually exclusive group now raises an exception. This nesting was
+ never supported, often failed to work correctly, and was unintentionally
+ exposed through inheritance.
Parser defaults
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index bc4ab67..2c1acb8 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -641,6 +641,14 @@ argparse
of :class:`!argparse.BooleanOptionalAction`.
They were deprecated since 3.12.
+* Calling :meth:`~argparse.ArgumentParser.add_argument_group` on an argument
+ group, and calling :meth:`~argparse.ArgumentParser.add_argument_group` or
+ :meth:`~argparse.ArgumentParser.add_mutually_exclusive_group` on a mutually
+ exclusive group now raise exceptions. This nesting was never supported,
+ often failed to work correctly, and was unintentionally exposed through
+ inheritance. This functionality has been deprecated since Python 3.11.
+ (Contributed by Savannah Ostrowski in :gh:`127186`.)
+
ast
---
diff --git a/Lib/argparse.py b/Lib/argparse.py
index f5a7342..d24fa72 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -1709,14 +1709,7 @@ class _ArgumentGroup(_ActionsContainer):
self._group_actions.remove(action)
def add_argument_group(self, *args, **kwargs):
- import warnings
- warnings.warn(
- "Nesting argument groups is deprecated.",
- category=DeprecationWarning,
- stacklevel=2
- )
- return super().add_argument_group(*args, **kwargs)
-
+ raise ValueError('argument groups cannot be nested')
class _MutuallyExclusiveGroup(_ArgumentGroup):
@@ -1737,15 +1730,8 @@ class _MutuallyExclusiveGroup(_ArgumentGroup):
self._container._remove_action(action)
self._group_actions.remove(action)
- def add_mutually_exclusive_group(self, *args, **kwargs):
- import warnings
- warnings.warn(
- "Nesting mutually exclusive groups is deprecated.",
- category=DeprecationWarning,
- stacklevel=2
- )
- return super().add_mutually_exclusive_group(*args, **kwargs)
-
+ def add_mutually_exclusive_group(self, **kwargs):
+ raise ValueError('mutually exclusive groups cannot be nested')
def _prog_name(prog=None):
if prog is not None:
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 69243fd..488a3a4 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -2997,6 +2997,13 @@ class TestGroupConstructor(TestCase):
self.assertEqual(msg, str(cm.warning))
self.assertEqual(cm.filename, __file__)
+ def test_nested_argument_group(self):
+ parser = argparse.ArgumentParser()
+ g = parser.add_argument_group()
+ self.assertRaisesRegex(ValueError,
+ 'argument groups cannot be nested',
+ g.add_argument_group)
+
# ===================
# Parent parser tests
# ===================
@@ -3297,6 +3304,14 @@ class TestMutuallyExclusiveGroupErrors(TestCase):
with self.assertRaises(ValueError):
parser.parse_args(['-h'])
+ def test_nested_mutex_groups(self):
+ parser = argparse.ArgumentParser(prog='PROG')
+ g = parser.add_mutually_exclusive_group()
+ g.add_argument("--spam")
+ self.assertRaisesRegex(ValueError,
+ 'mutually exclusive groups cannot be nested',
+ g.add_mutually_exclusive_group)
+
class MEMixin(object):
def test_failures_when_not_required(self):
@@ -3664,55 +3679,6 @@ class TestMutuallyExclusiveOptionalsAndPositionalsMixed(MEMixin, TestCase):
-c c help
'''
-class TestMutuallyExclusiveNested(MEMixin, TestCase):
-
- # Nesting mutually exclusive groups is an undocumented feature
- # that came about by accident through inheritance and has been
- # the source of many bugs. It is deprecated and this test should
- # eventually be removed along with it.
-
- def get_parser(self, required):
- parser = ErrorRaisingArgumentParser(prog='PROG')
- group = parser.add_mutually_exclusive_group(required=required)
- group.add_argument('-a')
- group.add_argument('-b')
- with warnings.catch_warnings():
- warnings.simplefilter('ignore', DeprecationWarning)
- group2 = group.add_mutually_exclusive_group(required=required)
- group2.add_argument('-c')
- group2.add_argument('-d')
- with warnings.catch_warnings():
- warnings.simplefilter('ignore', DeprecationWarning)
- group3 = group2.add_mutually_exclusive_group(required=required)
- group3.add_argument('-e')
- group3.add_argument('-f')
- return parser
-
- usage_when_not_required = '''\
- usage: PROG [-h] [-a A | -b B | [-c C | -d D | [-e E | -f F]]]
- '''
- usage_when_required = '''\
- usage: PROG [-h] (-a A | -b B | (-c C | -d D | (-e E | -f F)))
- '''
-
- help = '''\
-
- options:
- -h, --help show this help message and exit
- -a A
- -b B
- -c C
- -d D
- -e E
- -f F
- '''
-
- # We are only interested in testing the behavior of format_usage().
- test_failures_when_not_required = None
- test_failures_when_required = None
- test_successes_when_not_required = None
- test_successes_when_required = None
-
class TestMutuallyExclusiveOptionalOptional(MEMixin, TestCase):
def get_parser(self, required=None):
@@ -4883,25 +4849,6 @@ class TestHelpUsageNoWhitespaceCrash(TestCase):
usage = 'usage: PROG [-h]\n'
self.assertEqual(parser.format_usage(), usage)
- def test_nested_mutex_groups(self):
- parser = argparse.ArgumentParser(prog='PROG')
- g = parser.add_mutually_exclusive_group()
- g.add_argument("--spam")
- with warnings.catch_warnings():
- warnings.simplefilter('ignore', DeprecationWarning)
- gg = g.add_mutually_exclusive_group()
- gg.add_argument("--hax")
- gg.add_argument("--hox", help=argparse.SUPPRESS)
- gg.add_argument("--hex")
- g.add_argument("--eggs")
- parser.add_argument("--num")
-
- usage = textwrap.dedent('''\
- usage: PROG [-h] [--spam SPAM | [--hax HAX | --hex HEX] | --eggs EGGS]
- [--num NUM]
- ''')
- self.assertEqual(parser.format_usage(), usage)
-
def test_long_mutex_groups_wrap(self):
parser = argparse.ArgumentParser(prog='PROG')
g = parser.add_mutually_exclusive_group()
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-23-04-54-42.gh-issue-127133.WMoJjF.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-23-04-54-42.gh-issue-127133.WMoJjF.rst
new file mode 100644
index 0000000..56b496b
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-23-04-54-42.gh-issue-127133.WMoJjF.rst
@@ -0,0 +1,6 @@
+Calling :meth:`argparse.ArgumentParser.add_argument_group` on an argument group,
+and calling :meth:`argparse.ArgumentParser.add_argument_group` or
+:meth:`argparse.ArgumentParser.add_mutually_exclusive_group` on a mutually
+exclusive group now raise exceptions. This nesting was never supported, often
+failed to work correctly, and was unintentionally exposed through inheritance.
+This functionality has been deprecated since Python 3.11.