How to protect the python interpreter against termination when a called module/program issues sys.exit()

StackOverflow https://stackoverflow.com/questions/19804254

  •  04-07-2022
  •  | 
  •  

سؤال

In an interactively running python session I kick off modules/programs I wrote using argparse (because these modules are meant to be used as commands from the shell prompt as well).

If I call the module with the wrong args parameters, argparse correctly spits out an error as expected, but unfortunately arparse then calls sys.exit() which in turn terminates the outer python session.

That's not what I want. How can I protect the outer interactive python session from being terminated without changing the sys.exit() code in the inner module and without wrapping my module in code. - I'm looking for something like a switch I can set or something I can do to the interactive python session before I start it, so that sys.exit() does not terminate it.

Update from the comments:

The resaon why I ask this question is emacs's python-mode (python.el): It effectively "pastes" the complete code of the module file in the python session. If I want to wrap this in a try..except I need to indent the whole module before I wrap it and hand over to the python-session buffer in emacs.

Here a sample module I use (most of my command line utilities use a similar template):

#!/usr/bin/env python
"""\
tool to do stuff
"""
__author__ = """halloleo"""
__version__ = """0.1"""

import logging
import sys
import os

import clitools # my own helpers

#
# Options
#    
def argParser(locArgs = None):
    parser = clitools.HelpLogArgParser(description=__doc__)
    parser.add_argument('files', metavar='FILE', nargs='+',
                        help='file to work on')
    parser.add_loglevel_group()
    return parser.parse_args(locArgs)

def doStuff(file)
    # do stuff
    print file

#
# main
#
if __name__ == '__main__':

    args = argParser()     
    clitools.infoLoggerConfig(args.loglevel)
    logging.debug("args = %s" % args)          

    for f in args.files
        dostuff(f)

# Local Variables:
# leo-python-args-to-send: "--debug c:/tmp/testfile"
# End:

BTW, I know now, this approach works. I have implemented it, but it is kind of scary to indent the whole module code... Here is what at the end I handover to the python session:

import sys; sys.argv = '''scraper.py --debug c:/tmp/testfile'''.split()
try:
    #!/usr/bin/env python
    """\
    tool to do stuff
    """

    .
    .
    .

    # Local Variables:
    # leo-python-args-to-send: "--debug c:/tmp/testfile"
    # End:
except SystemExit, e:
    print "Terminated  with exit code", e
هل كانت مفيدة؟

المحلول

Use a try/except block catching SystemExit. For example:

import sys

try:
    sys.exit()
except SystemExit:
    print "Tried to sys.exit()"

نصائح أخرى

As documented in python's sys module, sys.exit does the following:

Exit from Python. This is implemented by raising the SystemExit exception, so cleanup actions specified by finally clauses of try statements are honored, and it is possible to intercept the exit attempt at an outer level.

So you can handle the exception in a try except block as any other exception.


argparse actually calls sys.exit from the exit method of argparse.ArgumentParser.

In some circumstances, it might be more worthwhile to subclass argparse.ArgumentParser to raise a more meaningful error:

import argparse
class BadCLIArgs(Exception):
    pass

class NonExitingArgumentParser(argparse.ArgumentParser):
    def exit(self, status=1, message=None):
        raise BadCLIArgs((status, message))

Or alternatively override error (as exit is also likely to be called for the --help flag.)

I've never really liked the approach of argparse to exit rather than raising a more meaningful exception.

I just came across this problem. It seems that the SystemExit error is raised when I was using

args = parser.parse_args(argv)

so my solution was simply to use a try-except with only this line:

def main(argv=None):
    ...
    try:
        args = parser.parse_args(argv)
    except SystemExit:
        return 0
    ...
    return 0

In my script code I use:

if __name__ == '__main__':
    sys.exit(main())

This still places a 0 in the interactive shell after displaying the error message, but it does successfully print the error message and stop the interactive shell from terminating.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top