Raise Better Exceptions in Python
Sun 17 November 2019 by Moshe ZadkaThere is a lot of Python code in the wild which does something like:
raise ValueError("Could not fraz the buzz:" f"{foo} is less than {quux}")
This is, in general, a bad idea. It does not matter if the exception is fairly generic, like ValueError or specific like CustomFormatParsingException.
Exceptions are not program panics. Program panics are things which should "never happen", and usually abort either the entire program, or at least an execution thread.
While exceptions sometimes do terminate the program, or the execution thread, with a traceback, they are different in that they can be caught.
The code that catches the exception will sometimes have a way to recover: for example, maybe it’s not that important for the application to fraz the buzz if foo is 0. In that case, the code would look like:
try: some_function() except ValueError as exc: if ???
Oh, right. We do not have direct access to foo. If we formatted better, using repr, at least we could tell the difference between 0 and "0": but we still would have to start parsing the representation out of the string.
Because of this, in general, it is better to raise exceptions like this:
raise ValueError("Could not fraz the buzz: foo is too small", foo, quux)
Note that all the exceptions defined in core Python already allow any number of arguments. Those arguments are available as exc.args, if exc is the exception object. If you do end up defining your custom exceptions, the easiest thing is to avoid overriding the __init__: this keeps this behavior.
Raising exceptions this way gives exception handling a lot of power: it can introspect foo, introspect quux and introspect the string. If by some reason the exception class is raised and we want to verify the reason, checking string equality, while not ideal, is still better than trying to match string parts or regular expression matching.
When the exception is presented to the user interface, in that case, it will not look as nice. Exceptions, in general, should reach the UI only in extreme circumstances. In those cases, having something that has as much information is useful for root cause analysis.
This is an update of an older blog post. Thanks to Mark Rice and Ben Nuttall for their improvement suggestions. All mistakes that are left are my responsibility.