https://github.com/python/cpython/commit/d5e28e19ae75465b563b5f88acf8accc92545470
commit: d5e28e19ae75465b563b5f88acf8accc92545470
branch: main
author: Bernát Gábor <[email protected]>
committer: savannahostrowski <[email protected]>
date: 2026-10-21T16:27:38Z
summary:
gh-144812: Make argparse group classes public (#158045)
files:
A Misc/NEWS.d/next/Library/2026-09-23-23-34-30.gh-issue-144812.ujptbVOI.rst
M Doc/library/argparse.rst
M Doc/whatsnew/3.16.rst
M Lib/argparse.py
M Lib/test/test_argparse.py
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 658209bc55fa14..3a8eab76ca0e2b 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -2072,6 +2072,19 @@ Argument groups
is now deprecated.
+.. class:: ArgumentGroup
+
+ The type of objects returned by :meth:`ArgumentParser.add_argument_group`.
+ Its :meth:`~ArgumentParser.add_argument` and
+ :meth:`~ArgumentParser.add_mutually_exclusive_group` methods work as on
+ :class:`ArgumentParser`. Use :meth:`~ArgumentParser.add_argument_group`
+ to create instances. Only the methods documented here are part of the
+ public API; other attributes and methods are implementation details.
+
+ .. versionadded:: next
+ The private ``_ArgumentGroup`` name remains as an alias.
+
+
Mutual exclusion
^^^^^^^^^^^^^^^^
@@ -2135,6 +2148,20 @@ Mutual exclusion
exposed through inheritance.
+.. class:: MutuallyExclusiveGroup
+
+ The type of objects returned by
+ :meth:`ArgumentParser.add_mutually_exclusive_group`.
+ Its :meth:`~ArgumentParser.add_argument` method works as on
+ :class:`ArgumentParser`. Use
+ :meth:`~ArgumentParser.add_mutually_exclusive_group` to create instances.
+ Only the ``add_argument`` method documented here is part of the public API;
+ other attributes and methods are implementation details.
+
+ .. versionadded:: next
+ The private ``_MutuallyExclusiveGroup`` name remains as an alias.
+
+
Parser defaults
^^^^^^^^^^^^^^^
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index c7ba9235a1a326..b29170b5b0b93d 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -126,6 +126,16 @@ New modules
Improved modules
================
+argparse
+--------
+
+* :class:`~argparse.ArgumentGroup` and
+ :class:`~argparse.MutuallyExclusiveGroup`, the types returned by
+ :meth:`~argparse.ArgumentParser.add_argument_group` and
+ :meth:`~argparse.ArgumentParser.add_mutually_exclusive_group`, are now
+ public. The ``_``-prefixed names remain as aliases.
+ (Contributed by Bernát Gábor in :gh:`144812`.)
+
asyncio
-------
diff --git a/Lib/argparse.py b/Lib/argparse.py
index 4cf5dca148c969..c01a6351f14482 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -67,6 +67,7 @@
__all__ = [
'ArgumentParser',
'ArgumentError',
+ 'ArgumentGroup',
'ArgumentTypeError',
'BooleanOptionalAction',
'FileType',
@@ -75,6 +76,7 @@
'RawDescriptionHelpFormatter',
'RawTextHelpFormatter',
'MetavarTypeHelpFormatter',
+ 'MutuallyExclusiveGroup',
'Namespace',
'Action',
'ONE_OR_MORE',
@@ -1699,12 +1701,12 @@ def add_argument(self, *args, **kwargs):
return self._add_action(action)
def add_argument_group(self, *args, **kwargs):
- group = _ArgumentGroup(self, *args, **kwargs)
+ group = ArgumentGroup(self, *args, **kwargs)
self._action_groups.append(group)
return group
def add_mutually_exclusive_group(self, **kwargs):
- group = _MutuallyExclusiveGroup(self, **kwargs)
+ group = MutuallyExclusiveGroup(self, **kwargs)
self._mutually_exclusive_groups.append(group)
return group
@@ -1892,7 +1894,7 @@ def _check_help(self, action):
raise ValueError('badly formed help string') from exc
-class _ArgumentGroup(_ActionsContainer):
+class ArgumentGroup(_ActionsContainer):
def __init__(self, container, title=None, description=None, **kwargs):
if 'prefix_chars' in kwargs:
@@ -1907,7 +1909,7 @@ def __init__(self, container, title=None,
description=None, **kwargs):
update('conflict_handler', container.conflict_handler)
update('prefix_chars', container.prefix_chars)
update('argument_default', container.argument_default)
- super_init = super(_ArgumentGroup, self).__init__
+ super_init = super(ArgumentGroup, self).__init__
super_init(description=description, **kwargs)
# group attributes
@@ -1924,21 +1926,21 @@ def __init__(self, container, title=None,
description=None, **kwargs):
self._mutually_exclusive_groups = container._mutually_exclusive_groups
def _add_action(self, action):
- action = super(_ArgumentGroup, self)._add_action(action)
+ action = super(ArgumentGroup, self)._add_action(action)
self._group_actions.append(action)
return action
def _remove_action(self, action):
- super(_ArgumentGroup, self)._remove_action(action)
+ super(ArgumentGroup, self)._remove_action(action)
self._group_actions.remove(action)
def add_argument_group(self, *args, **kwargs):
raise ValueError('argument groups cannot be nested')
-class _MutuallyExclusiveGroup(_ArgumentGroup):
+class MutuallyExclusiveGroup(ArgumentGroup):
def __init__(self, container, required=False):
- super(_MutuallyExclusiveGroup, self).__init__(container)
+ super(MutuallyExclusiveGroup, self).__init__(container)
self.required = required
self._container = container
@@ -1957,6 +1959,10 @@ def _remove_action(self, action):
def add_mutually_exclusive_group(self, **kwargs):
raise ValueError('mutually exclusive groups cannot be nested')
+# Kept for code that imports the pre-3.16 private names.
+_ArgumentGroup = ArgumentGroup
+_MutuallyExclusiveGroup = MutuallyExclusiveGroup
+
def _prog_name(prog=None):
if prog is not None:
return prog
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 3abdb3244570a5..e1c4cf6561fc77 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -3246,6 +3246,20 @@ def test_nested_argument_group(self):
'argument groups cannot be nested',
g.add_argument_group)
+ def test_group_types(self):
+ parser = argparse.ArgumentParser()
+ group = parser.add_argument_group()
+ self.assertIs(type(group), argparse.ArgumentGroup)
+ self.assertIs(type(parser.add_mutually_exclusive_group()),
+ argparse.MutuallyExclusiveGroup)
+ self.assertIs(type(group.add_mutually_exclusive_group()),
+ argparse.MutuallyExclusiveGroup)
+
+ def test_private_aliases(self):
+ self.assertIs(argparse._ArgumentGroup, argparse.ArgumentGroup)
+ self.assertIs(argparse._MutuallyExclusiveGroup,
+ argparse.MutuallyExclusiveGroup)
+
# ===================
# Parent parser tests
# ===================
diff --git
a/Misc/NEWS.d/next/Library/2026-09-23-23-34-30.gh-issue-144812.ujptbVOI.rst
b/Misc/NEWS.d/next/Library/2026-09-23-23-34-30.gh-issue-144812.ujptbVOI.rst
new file mode 100644
index 00000000000000..d24f028fdbeaae
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-23-23-34-30.gh-issue-144812.ujptbVOI.rst
@@ -0,0 +1,3 @@
+Make :class:`argparse.ArgumentGroup` and
+:class:`argparse.MutuallyExclusiveGroup` public. The private
+``_ArgumentGroup`` and ``_MutuallyExclusiveGroup`` names remain as aliases.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]