bpo-9938: Add optional keyword argument exit_on_error to argparse.ArgumentParser by shihai1991 · Pull Request #15362 · python/cpython · GitHub
Skip to content
30 changes: 29 additions & 1 deletion Doc/library/argparse.rst
26 changes: 17 additions & 9 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1623,6 +1623,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
- conflict_handler -- String indicating how to handle conflicts
- add_help -- Add a -h/-help option
- 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,
Expand All @@ -1637,7 +1639,8 @@ def __init__(self,
argument_default=None,
conflict_handler='error',
add_help=True,
allow_abbrev=True):
allow_abbrev=True,
exit_on_error=True):

superinit = super(ArgumentParser, self).__init__
superinit(description=description,
Expand All @@ -1656,6 +1659,7 @@ def __init__(self,
self.fromfile_prefix_chars = fromfile_prefix_chars
self.add_help = add_help
self.allow_abbrev = allow_abbrev
self.exit_on_error = exit_on_error

add_group = self.add_argument_group
self._positionals = add_group(_('positional arguments'))
Expand Down Expand Up @@ -1786,15 +1790,19 @@ def parse_known_args(self, args=None, namespace=None):
setattr(namespace, dest, self._defaults[dest])

# 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)
if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR))
delattr(namespace, _UNRECOGNIZED_ARGS_ATTR)
return namespace, args
except ArgumentError:
err = _sys.exc_info()[1]
self.error(str(err))

if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR))
delattr(namespace, _UNRECOGNIZED_ARGS_ATTR)
return namespace, args

def _parse_known_args(self, arg_strings, namespace):
# replace arg strings that are file references
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -5222,6 +5222,21 @@ def test_help_with_metavar(self):
'''))


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():
support.run_unittest(__name__)
# Remove global references to avoid looking like we have refleaks.
Expand Down