Python: using sys.exit or SystemExit differences and suggestions
Reading online some programmers use sys.exit
, others use SystemExit
.
Sorry for the basic question:
- What is the difference?
- 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)
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).
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)
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.
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)
According to documentation sys.exit(s)
effectively does raise SystemExit(s)
, so it's pretty much the same thing.