Commit f545638b authored by Hai Shi's avatar Hai Shi Committed by Miss Islington (bot)

bpo-9938: Add optional keyword argument exit_on_error to argparse.ArgumentParser (GH-15362)

Co-Authored-by: default avatarXuanji Li <xuanji@gmail.com>


https://bugs.python.org/issue9938



Automerge-Triggered-By: @matrixise
parent 2d32bf1e
...@@ -142,7 +142,7 @@ ArgumentParser objects ...@@ -142,7 +142,7 @@ ArgumentParser objects
formatter_class=argparse.HelpFormatter, \ formatter_class=argparse.HelpFormatter, \
prefix_chars='-', fromfile_prefix_chars=None, \ prefix_chars='-', fromfile_prefix_chars=None, \
argument_default=None, conflict_handler='error', \ argument_default=None, conflict_handler='error', \
add_help=True, allow_abbrev=True) add_help=True, allow_abbrev=True, exit_on_error=True)
Create a new :class:`ArgumentParser` object. All parameters should be passed Create a new :class:`ArgumentParser` object. All parameters should be passed
as keyword arguments. Each parameter has its own more detailed description as keyword arguments. Each parameter has its own more detailed description
...@@ -179,6 +179,9 @@ ArgumentParser objects ...@@ -179,6 +179,9 @@ ArgumentParser objects
* allow_abbrev_ - Allows long options to be abbreviated if the * allow_abbrev_ - Allows long options to be abbreviated if the
abbreviation is unambiguous. (default: ``True``) abbreviation is unambiguous. (default: ``True``)
* exit_on_error_ - Determines whether or not ArgumentParser exits with
error info when an error occurs. (default: ``True``)
.. versionchanged:: 3.5 .. versionchanged:: 3.5
*allow_abbrev* parameter was added. *allow_abbrev* parameter was added.
...@@ -186,6 +189,9 @@ ArgumentParser objects ...@@ -186,6 +189,9 @@ ArgumentParser objects
In previous versions, *allow_abbrev* also disabled grouping of short In previous versions, *allow_abbrev* also disabled grouping of short
flags such as ``-vv`` to mean ``-v -v``. flags such as ``-vv`` to mean ``-v -v``.
.. versionchanged:: 3.9
*exit_on_error* parameter was added.
The following sections describe how each of these are used. The following sections describe how each of these are used.
...@@ -647,6 +653,28 @@ the help options:: ...@@ -647,6 +653,28 @@ the help options::
+h, ++help show this help message and exit +h, ++help show this help message and exit
exit_on_error
^^^^^^^^^^^^^
Normally, when you pass an invalid argument list to the :meth:`~ArgumentParser.parse_args`
method of an :class:`ArgumentParser`, it will exit with error info.
If the user would like catch errors manually, the feature can be enable by setting
``exit_on_error`` to ``False``::
>>> parser = argparse.ArgumentParser(exit_on_error=False)
>>> parser.add_argument('--integers', type=int)
_StoreAction(option_strings=['--integers'], dest='integers', nargs=None, const=None, default=None, type=<class 'int'>, choices=None, help=None, metavar=None)
>>> try:
... parser.parse_args('--integers a'.split())
... except argparse.ArgumentError:
... print('Catching an argumentError')
...
Catching an argumentError
.. versionadded:: 3.9
The add_argument() method The add_argument() method
------------------------- -------------------------
......
...@@ -1630,6 +1630,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): ...@@ -1630,6 +1630,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
- conflict_handler -- String indicating how to handle conflicts - conflict_handler -- String indicating how to handle conflicts
- add_help -- Add a -h/-help option - add_help -- Add a -h/-help option
- allow_abbrev -- Allow long options to be abbreviated unambiguously - allow_abbrev -- Allow long options to be abbreviated unambiguously
- exit_on_error -- Determines whether or not ArgumentParser exits with
error info when an error occurs
""" """
def __init__(self, def __init__(self,
...@@ -1644,7 +1646,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): ...@@ -1644,7 +1646,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
argument_default=None, argument_default=None,
conflict_handler='error', conflict_handler='error',
add_help=True, add_help=True,
allow_abbrev=True): allow_abbrev=True,
exit_on_error=True):
superinit = super(ArgumentParser, self).__init__ superinit = super(ArgumentParser, self).__init__
superinit(description=description, superinit(description=description,
...@@ -1663,6 +1666,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): ...@@ -1663,6 +1666,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
self.fromfile_prefix_chars = fromfile_prefix_chars self.fromfile_prefix_chars = fromfile_prefix_chars
self.add_help = add_help self.add_help = add_help
self.allow_abbrev = allow_abbrev self.allow_abbrev = allow_abbrev
self.exit_on_error = exit_on_error
add_group = self.add_argument_group add_group = self.add_argument_group
self._positionals = add_group(_('positional arguments')) self._positionals = add_group(_('positional arguments'))
...@@ -1793,15 +1797,19 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): ...@@ -1793,15 +1797,19 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
setattr(namespace, dest, self._defaults[dest]) setattr(namespace, dest, self._defaults[dest])
# parse the arguments and exit if there are any errors # parse the arguments and exit if there are any errors
try: if self.exit_on_error:
try:
namespace, args = self._parse_known_args(args, namespace)
except ArgumentError:
err = _sys.exc_info()[1]
self.error(str(err))
else:
namespace, args = self._parse_known_args(args, namespace) namespace, args = self._parse_known_args(args, namespace)
if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR))
return namespace, args delattr(namespace, _UNRECOGNIZED_ARGS_ATTR)
except ArgumentError: return namespace, args
err = _sys.exc_info()[1]
self.error(str(err))
def _parse_known_args(self, arg_strings, namespace): def _parse_known_args(self, arg_strings, namespace):
# replace arg strings that are file references # replace arg strings that are file references
......
...@@ -5262,6 +5262,21 @@ class TestWrappingMetavar(TestCase): ...@@ -5262,6 +5262,21 @@ class TestWrappingMetavar(TestCase):
''')) '''))
class TestExitOnError(TestCase):
def setUp(self):
self.parser = argparse.ArgumentParser(exit_on_error=False)
self.parser.add_argument('--integers', metavar='N', type=int)
def test_exit_on_error_with_good_args(self):
ns = self.parser.parse_args('--integers 4'.split())
self.assertEqual(ns, argparse.Namespace(integers=4))
def test_exit_on_error_with_bad_args(self):
with self.assertRaises(argparse.ArgumentError):
self.parser.parse_args('--integers a'.split())
def test_main(): def test_main():
support.run_unittest(__name__) support.run_unittest(__name__)
# Remove global references to avoid looking like we have refleaks. # Remove global references to avoid looking like we have refleaks.
......
Add optional keyword argument ``exit_on_error`` for :class:`ArgumentParser`.
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