diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index e050d6298b6ff6..9b587908ea8b4e 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -2090,8 +2090,9 @@ Exiting methods .. method:: ArgumentParser.error(message) - This method prints a usage message including the *message* to the - standard error and terminates the program with a status code of 2. + When exit on error is ``True`` this method prints a usage message including + the *message* to the standard error and terminates the program with a status + code of 2, otherwise raises an ArgumentError. Intermixed parsing diff --git a/Lib/argparse.py b/Lib/argparse.py index 429a72ab7841e7..040d6bf03f74dd 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1872,13 +1872,11 @@ 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 - if self.exit_on_error: - try: - namespace, args = self._parse_known_args(args, namespace) - except ArgumentError as err: - self.error(str(err)) - else: + try: namespace, args = self._parse_known_args(args, namespace) + except ArgumentError: + err = _sys.exc_info()[1] + self.error(str(err)) if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) @@ -2588,12 +2586,16 @@ def exit(self, status=0, message=None): def error(self, message): """error(message: string) - Prints a usage message incorporating the message to stderr and - exits. + When exit_on_error is true prints a usage message + incorporating the message to stderr and exits, otherwise raises + an ArgumentError. If you override this in a subclass, it should not return -- it should either exit or raise an exception. """ + if not self.exit_on_error: + raise ArgumentError(None, message) + self.print_usage(_sys.stderr) args = {'prog': self.prog, 'message': message} self.exit(2, _('%(prog)s: error: %(message)s\n') % args) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 1f03b7fb24261b..94b4da04eb246c 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -5541,7 +5541,7 @@ 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()) @@ -5551,6 +5551,14 @@ 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_args(self): + with self.assertRaises(argparse.ArgumentError): + self.parser.parse_args([]) + + def test_exit_on_error_unknown_args(self): + with self.assertRaises(argparse.ArgumentError): + self.parser.parse_args("--integers 1 --unknown-arg".split()) + def tearDownModule(): # Remove global references to avoid looking like we have refleaks. diff --git a/Misc/NEWS.d/next/Library/2021-07-22-21-56-34.bpo-41255.tbSAVo.rst b/Misc/NEWS.d/next/Library/2021-07-22-21-56-34.bpo-41255.tbSAVo.rst new file mode 100644 index 00000000000000..aad7cf303a8903 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-07-22-21-56-34.bpo-41255.tbSAVo.rst @@ -0,0 +1 @@ +fixed errors with argparse exit_on_error causing unexpected behavior \ No newline at end of file