92

Some programmers use sys.exit, others use SystemExit.

  1. What is the difference?
  2. When do I need to use SystemExit or sys.exit inside a function?

Example:

ref = osgeo.ogr.Open(reference)
if ref is None:
    raise SystemExit('Unable to open %s' % reference)

or:

ref = osgeo.ogr.Open(reference)
if ref is None:
    print('Unable to open %s' % reference)
    sys.exit(-1)
user4157124
  • 2,809
  • 13
  • 27
  • 42
Gianni Spear
  • 7,033
  • 22
  • 82
  • 131
  • 8
    From https://docs.python.org/3.0/library/sys.html#sys.exit "Most systems require it to be in the range 0-127, and produce undefined results otherwise." So `sys.exit(-1)` is maybe not the best idea. – Christian Jul 07 '15 at 18:14
  • 3
    sys.exit() accepts the same optional argument as SystemExit. So, it's more correct to compare SystemExit('Unable to open %s' % reference) with sys.exit('Unable to open %s' % reference). In this case (the argument is not integer nor None), they both print it to stderr and exit the process with code 1. Useful to exit process with an error message by one-liner, if you don't mind the exact exit code. – Alex Che Apr 04 '17 at 13:13
  • The concern about `exit(-1)` is not theoretical: POSIX systems use exit code -1 to indicate that the process was killed by SIGHUP, and will probably mangle your -1 to something else. – Alex Shpilkin Apr 26 '19 at 11:15
  • 1
    Considering your program exits because of being unable to open something... Maybe you should be throwing an `IOError` or another applicable error instead of `SystemExit`? – Arthur Khazbs Aug 08 '20 at 18:43

7 Answers7

45

No practical difference, but there's another difference in your example code - print goes to standard out, but the exception text goes to standard error (which is probably what you want).

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
  • 1
    Hi RichieHindle!..I [**read here**](http://blog.engledew.com/post/344116632/killing-python-exiting-without-using-systemexit) There is difference between two in multi-threaded python code. What I understood: The Call of SystemExit in multi-thread will destroy thread only? Am I correct? – Grijesh Chauhan Dec 21 '12 at 16:04
  • 2
    @GrijeshChauhan: They are still the same. Either will cause the current thread to exit. (The "change" talked about in that blog post is the difference between the main thread and a background thread, not between `exit` and `SystemExit`.) – RichieHindle Dec 21 '12 at 16:29
42

sys.exit(s) is just shorthand for raise SystemExit(s), as described in the former's docstring; try help(sys.exit). So, instead of either one of your example programs, you can do

sys.exit('Unable to open %s' % reference)
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
32

There are 3 exit functions, in addition to raising SystemExit.

The underlying one is os._exit, which requires 1 int argument, and exits immediately with no cleanup. It's unlikely you'll ever want to touch this one, but it is there.

sys.exit is defined in sysmodule.c and just runs PyErr_SetObject(PyExc_SystemExit, exit_code);, which is effectively the same as directly raising SystemExit. In fine detail, raising SystemExit is probably faster, since sys.exit requires an LOAD_ATTR and CALL_FUNCTION vs RAISE_VARARGS opcalls. Also, raise SystemExit produces slightly smaller bytecode (4bytes less), (1 byte extra if you use from sys import exit since sys.exit is expected to return None, so includes an extra POP_TOP).

The last exit function is defined in site.py, and aliased to exit or quit in the REPL. It's actually an instance of the Quitter class (so it can have a custom __repr__, so is probably the slowest running. Also, it closes sys.stdin prior to raising SystemExit, so it's recommended for use only in the REPL.

As for how SystemExit is handled, it eventually causes the VM to call os._exit, but before that, it does some cleanup. It also runs atexit._run_exitfuncs() which runs any callbacks registered via the atexit module. Calling os._exit directly bypasses the atexit step.

Perkins
  • 2,409
  • 25
  • 23
  • Another advantage of raising the SystemExit manually is that not all static analyzers understand that the execution will be interrupted after calling sys.exit. This is important, for example, so that the analyzer can detect execution paths where a referenced variable may not be defined. – MMM Jul 03 '22 at 14:58
15

My personal preference is that at the very least SystemExit is raised (or even better - a more meaningful and well documented custom exception) and then caught as close to the "main" function as possible, which can then have a last chance to deem it a valid exit or not. Libraries/deeply embedded functions that have sys.exit is just plain nasty from a design point of view. (Generally, exiting should be "as high up" as possible)

Jon Clements
  • 138,671
  • 33
  • 247
  • 280
  • 1
    Note that you can catch `SystemExit` exceptions in python, which would allow proper clean up of anything needed. Of course, if you do that, make sure any nested exits use the `raise SystemExit(code)` rather than `sys.exit`, since it would be rather non-obvious if `try:... except SystemExit` caught `sys.exit` calls (anyone not aware of what exactly `sys.exit` does will probably be confused). – Perkins Apr 01 '16 at 20:09
6

According to documentation sys.exit(s) effectively does raise SystemExit(s), so it's pretty much the same thing.

piokuc
  • 25,594
  • 11
  • 72
  • 102
6

While the difference has been answered by many answers, Cameron Simpson makes an interesting point in https://mail.python.org/pipermail/python-list/2016-April/857869.html:

TL;DR: It's better to just raise a "normal" exception, and use SystemExit or sys.exit only at the top levels of a script.

I m on python 2.7 and Linux , I have a simple code need suggestion if I I could replace sys.exit(1) with raise SystemExit .

==Actual code==

def main():    
    try:
       create_logdir()
       create_dataset()
       unittest.main()    
     except Exception as e:
       logging.exception(e)
       sys.exit(EXIT_STATUS_ERROR)

if __name__ == '__main__':    main()

==Changed Code==

def main():    
    try:
       create_logdir()
       create_dataset()
       unittest.main()    
    except Exception as e:
       logging.exception(e)
       raise SystemExit

if __name__ == '__main__':    
    main()

I am against both of these personally. My preferred pattern is like this:

  def main(argv):
    try:
      ...
    except Exception as e:
      logging.exception(e)
      return 1

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

Notice that main() is back to being a normal function with normal returns.

Also, most of us would avoid the "except Exception" and just let a top level except bubble out: that way you get a stack backtrace for debugging. I agree it prevents logging the exception and makes for uglier console output, but I think it is a win. And if you do want to log the exception there is always this:

try: ... except Exception as e: logging.exception(e) raise

to recite the exception into the log and still let it bubble out normally.

The problem with the "except Exception" pattern is that it catches and hides every exception, not merely the narrow set of specific exceptions that you understand.

Finally, it is frowned upon to raise a bare Exception class. In python 3 I believe it is actually forbidden, so it is nonportable anyway. But even In Python to it is best to supply an Exception instance, not the class:

raise SystemExit(1)

  1. All the functions in try block have exception bubbled out using raise

Example for create_logdir() here is the function definition

def create_logdir():

try: os.makedirs(LOG_DIR) except OSError as e: sys.stderr.write("Failed to create log directory...Exiting !!!") raise print "log file: " + corrupt_log return True

def main(): try: create_logdir() except Exception as e: logging.exception(e) raise SystemExit

(a) In case if create_logdir() fails we will get the below error ,is this fine or do I need to improve this code.

Failed to create log directory...Exiting !!!ERROR:root:[Errno 17] File exists: '/var/log/dummy'

Traceback (most recent call last): File "corrupt_test.py", line 245, in main create_logdir() File "corrupt_test.py", line 53, in create_logdir os.makedirs(LOG_DIR) File "/usr/local/lib/python2.7/os.py", line 157, in makedirs OSError: [Errno 17] File exists: '/var/log/dummy'

I prefer the bubble out approach, perhap with a log or warning messages as you have done, eg:

logging.exception("create_logdir failed: makedirs(%r): %s" % (LOG_DIR, e)) raise

(Also not that that log message records more context: context is very useful when debugging problems.)

For very small scripts sys.stderr.write is ok, but in general any of your functions that turned out to be generally useful might migrate into a library in order to be reused; consider that stderr is not always the place for messages; instead reading for the logging module with error() or wanr() or exception() as appropriate. There is more scope for configuring where the output goes that way without wiring it into your inner functions.

  1. Can I have just raise , instead of SystemExit or sys.exit(1) . This looks wrong to me

def main():

try: create_logdir() except Exception as e logging.exception(e) raise

This is what I would do, myself.

Think: has the exception been "handled", meaning has the situation been dealt with because it was expected? If not, let the exception bubble out so that the user knows that something not understood by the program has occurred.

Finally, it is generally bad to SystemExit or sys.exit() from inside anything other than the outermost main() function. And I resist it even there; the main function, if written well, may often be called from somewhere else usefully, and that makes it effectively a library function (it has been reused). Such a function should not unilaterally abort the program. How rude! Instead, let the exception bubble out: perhaps the caller of main() expects it and can handle it. By aborting and not "raise"ing, you have deprived the caller of the chance to do something appropriate, even though you yourself (i.e. "main") do not know enough context to handle the exception.

So I am for "raise" myself. And then only because you want to log the error. If you didn't want to log the exception you could avoid the try/except entirely and have simpler code: let the caller worry about unhandled exceptions!

serv-inc
  • 35,772
  • 9
  • 166
  • 188
4

SystemExit is an exception, which basically means that your progam had a behavior such that you want to stop it and raise an error. sys.exit is the function that you can call to exit from your program, possibily giving a return code to the system.

EDIT: they are indeed the same thing, so the only difference is in the logic behind in your program. An exception is some kind of "unwanted" behaviour, whether a call to a function is, from a programmer point of view, more of a "standard" action.

Cynical
  • 9,328
  • 1
  • 15
  • 30
  • 2
    There is a difference between `Exception` and `Error`. An `Exception` is something unusual (`SIGINT`, a disconnected network connection, a `StopIteration Exception`). They're just a fancy way to do `escape continuations`. An `Error` is program misbehavior, (i.e. running out of memory). Python implements both `Exception`s and `Error`s as subclasses of `Exception`, but `Error`s contain `Error` in the name. `SystemExit` is an `Exception`, it only happens once. If you're exiting from nested code, raising `SystemExit` might be more obvious that it's intentional to exit from the middle of something. – Perkins Apr 01 '16 at 20:06