Skip to content

gh-85427: Prevent exits if ArgumentParser.exit_on_error is False #30832

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,11 @@ def parse_args(self, args=None, namespace=None):
args, argv = self.parse_known_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s')
if not self.exit_on_error:
raise ArgumentError(
None,
msg % ' '.join(argv)
)
self.error(msg % ' '.join(argv))
return args

Expand Down Expand Up @@ -2139,8 +2144,11 @@ def consume_positionals(start_index):
self._get_value(action, action.default))

if required_actions:
self.error(_('the following arguments are required: %s') %
', '.join(required_actions))
raise ArgumentError(
None,
_('the following arguments are required: %s') %
', '.join(required_actions)
)

# make sure all required groups had one option present
for group in self._mutually_exclusive_groups:
Expand All @@ -2155,7 +2163,10 @@ def consume_positionals(start_index):
for action in group._group_actions
if action.help is not SUPPRESS]
msg = _('one of the arguments %s is required')
self.error(msg % ' '.join(names))
raise ArgumentError(
None,
msg % ' '.join(names)
)

# return the updated namespace and the extra arguments
return namespace, extras
Expand Down Expand Up @@ -2384,7 +2395,10 @@ def parse_intermixed_args(self, args=None, namespace=None):
args, argv = self.parse_known_intermixed_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
formatted_msg = msg % ' '.join(argv)
if self.exit_on_error:
self.error(formatted_msg)
raise ArgumentError(None, formatted_msg)
return args

def parse_known_intermixed_args(self, args=None, namespace=None):
Expand Down
25 changes: 24 additions & 1 deletion Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -5594,7 +5594,8 @@ class TestExitOnError(TestCase):

def setUp(self):
self.parser = argparse.ArgumentParser(exit_on_error=False)
self.parser.add_argument('--integers', metavar='N', type=int)
self.parser.add_argument(
'--integers', metavar='N', type=int, required=True)

def test_exit_on_error_with_good_args(self):
ns = self.parser.parse_args('--integers 4'.split())
Expand All @@ -5604,6 +5605,28 @@ def test_exit_on_error_with_bad_args(self):
with self.assertRaises(argparse.ArgumentError):
self.parser.parse_args('--integers a'.split())

def test_exit_on_error_missing_required_arg(self):
msg = 'the following arguments are required: --integers'
with self.assertRaisesRegex(argparse.ArgumentError, msg):
self.parser.parse_args([])

def test_exit_on_error_unknown_arg(self):
msg = 'unrecognized arguments: --unknown'
with self.assertRaisesRegex(argparse.ArgumentError, msg):
self.parser.parse_args('--integers 4 --unknown'.split())
with self.assertRaisesRegex(argparse.ArgumentError, msg.replace('--', '')):
self.parser.parse_intermixed_args('--integers 4 unknown'.split())

def test_exit_on_error_mutually_exclusive_group(self):
other_parser = argparse.ArgumentParser(exit_on_error=False)
group = other_parser.add_mutually_exclusive_group(required=True)
group.add_argument('--up', action='store_true')
group.add_argument('--down', action='store_true')

msg = 'one of the arguments --up --down is required'
with self.assertRaisesRegex(argparse.ArgumentError, msg):
other_parser.parse_args([])


def tearDownModule():
# Remove global references to avoid looking like we have refleaks.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The :attr:`exit_on_error` attribute of :class:`argparse.ArgumentParser` did
not always avoid exiting when set to False.