1

I'm considering adding argparse to existing batch jobs which run python. These jobs take command line arguments which can vary depending on how the jobs are to run. I'd like to use the named argument feature in argparse but instead of exiting the job if an argument is eg. missing or misnamed, I'd like to trap that error and hand the subsequent processing off to existing python routines which handle the job exit. These routines create logs, backout db transactions, flush files to disk and email whoever is responsible for the job of the problem encountered.

I've tried using python try except block (below) to trap the error but it fails. I've also seen solutions which involve subclassing argparse but this results in a much more complicated code base than prior to introducing argparse and is therefore counter-productive.

Is there a simple solution to this problem I'm missing? Many thanks.

try:
    args = my_parser.parse_args()
except Exception as parseError:
    do my stuff
Barmar
  • 741,623
  • 53
  • 500
  • 612
Niall
  • 51
  • 1
  • 4

2 Answers2

1

You need to set the exit_on_error option to False when creating the ArgumentParser object. Then it will raise an exception instead of exiting the script.

my_parser = argparse.ArgumentParser(exit_on_error=False)
# Add options to my_parser
try:
    args = my_parser.parse_args()
except argparse.ArgumentError as parseError:
    # do my stuff

If you're using an older version of argparse that doesn't have this option, you can make a subclass of ArgumentParser that overrides the ArgumentParser.exit() method to raise ane exception instead of exiting.

class ErrorCatchingArgumentParser(argparse.ArgumentParser):
    def exit(self, status=0, message=None):
        if status:
            raise Exception(f'Exiting because of an error: {message}')
        exit(status)

my_parser = ErrorCatchingArgumentParser()
# Add options to my_parser
try:
    args = my_parser.parse_args()
except Exception as parseError:
    # do my stuff
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • `exit_on_error` is a relatively new addition. For older versions is possible to wrap the `parse_args` in an `if/execpt` and capture the `sys.exit()`. `ipython` interactive sessions do this. Or you can customize the `parser.exit` method. https://docs.python.org/3/library/argparse.html#exiting-methods – hpaulj Mar 17 '21 at 20:30
  • @hpaulj I assume you mean `try/except`, which is what the OP tried to do but it didn't work. Which suggests he's using the newer version. – Barmar Mar 17 '21 at 20:32
  • `except SystemExit` works. `SystemExit` is not a subclass of `Exception`. – hpaulj Mar 17 '21 at 20:38
  • exit_on_error was not added until 3.9. (https://docs.python.org/3/library/argparse.html) but I'm confined to 3.6. so exit_on_error generates TypeError: __init__() got an unexpected keyword argument 'exit_on_error' – Niall Mar 18 '21 at 15:49
0

In an ipython session:

In [3]: parser = argparse.ArgumentParser()
In [4]: parser.parse_args(['foo'])
usage: ipython3 [-h]
ipython3: error: unrecognized arguments: foo
An exception has occurred, use %tb to see the full traceback.
SystemExit: 2

/usr/local/lib/python3.8/dist-packages/IPython/core/interactiveshell.py:3426: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
  warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)
In [5]: try:
   ...:     parser.parse_args(['foo'])
   ...: except SystemExit:
   ...:     pass
   ...: 
usage: ipython3 [-h]
ipython3: error: unrecognized arguments: foo

And capturing the traceback:

In [12]: import sys
In [13]: try:
    ...:     parser.parse_args(['foo'])
    ...: except SystemExit:
    ...:     tb = sys.exc_info()
    ...: 
    ...: 
usage: ipython3 [-h]
ipython3: error: unrecognized arguments: foo
In [14]: print(tb)
(<class 'SystemExit'>, SystemExit(2), <traceback object at 0x7f9ffe415cc0>)
hpaulj
  • 221,503
  • 14
  • 230
  • 353