About the exit_on_error setting in argparse

Per the documentation:

exit_on_error
Normally, when you pass an invalid argument list to the parse_args() method of an ArgumentParser, it will exit with error info.
If the user would like to catch errors manually, the feature can be enabled 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

The example works as described, but other kinds of invalid inputs don’t work:

$ python
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import argparse
>>> 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, required=False, help=None, metavar=None)
>>> try:
...     parser.parse_args('invalid arguments'.split())
... except argparse.ArgumentError:
...     print('Catching an argumentError')
... 
usage: [-h] [--integers INTEGERS]
: error: unrecognized arguments: invalid arguments
$

Why does it exit with this input, instead of still raising an exception?

Is this intended? How shall I work around it?

I’ve always found this default very annoying, because a) in usual contexts where argparse is used in a script, the program would exit naturally anyway; b) it becomes really hard to use the REPL to prototype or debug argparse usage.

1 Like

The problem is that the parse_args method relies on parse_known_args to honor exit_on_error=False and does not raise ArgumentError itself when there are unrecognized arguments.

I’ve create a PR for this issue, and in the mean time you can override parse_args to handle the scenario properly:

import argparse

class NewArgumentParser(argparse.ArgumentParser):
    def parse_args(self, args=None, namespace=None):
        args, argv = self.parse_known_args(args, namespace)
        if argv:
            msg = 'unrecognized arguments: %s' % ' '.join(argv)
            if self.exit_on_error:
                self.error(msg)
            raise argparse.ArgumentError(None, msg)
        return args

parser = NewArgumentParser(exit_on_error=False)
parser.parse_args('invalid arguments'.split())

which outputs:

argparse.ArgumentError: unrecognized arguments: invalid arguments
2 Likes