When writing command line applications, I often end up defining some classes of ‘input error’, which relate to bad input - from command line arguments, config files, etc. The traceback to the code that raised them isn’t important, so the top level of my code does something like this to hide tracebacks for those errors:
try: main() except InputError as e: sys.exit(str(e)) # Print the message to stderr and exit with status 1
This is analogous to the difference between HTTP 5xx errors (something went wrong on the server) and 4xx errors (the client made a ‘bad’ request in some way).
So my proposal is that Python’s default
excepthook should do something similar - either with a new built-in exception type such as
InputError, or some parameter or attribute you can set on any exception to suppress the traceback (akin to the existing
__suppress_context__ attribute). This would come with an override, an environment variable or a python command line option that would show the normally-hidden traceback.
Why don’t I use
SystemExit, which already behaves this way? Mostly gut feeling, but I’ll try to justify that a bit. SystemExit is a special exception type for cleanly exiting a running program, and catching it is a code smell, not least because it pushes developers towards extreme measures if they really want to exit. Code parsing and validating a config file may know this config file is invalid because XYZ, but it’s not up to the parsing function to decide that the application should terminate as a result. It’s the difference between describing a problem and proposing a solution.
- Testing the code which raises this error: I want to catch and check for a semantically meaningful exception.
- The same code might be used in a command-line context where it’s appropriate to exit, and in e.g. an interactive GUI where it’s appropriate to display an error message but not exit.
- It might have meaning in other contexts, e.g. web applications might want to map
InputErrorexceptions to HTTP 4xx errors.
What’s wrong with defining the exception type and handling it in the application, as I already do? It’s OK so long as the code raising the exception is part of the application, but if the parsing/validation happens in a separate library, there’s no standard way to do this. E.g.
argparse currently calls
sys.exit() if there are problems with the arguments it’s parsing - this is making an assumption about the context in which it’s called.
So what I’m suggesting is a standard means for code raising an exception to indicate that if this goes uncaught and is shown to the user, the error message should provide enough detail about the problem without a traceback, without necessarily telling the application that it should quit.