You probably shouldn’t be using except:
or except Exception:
in
Python, sometimes called ‘catch-all exceptions’ or ‘naked excepts’.
I’ve tried to find a good article or blog post discussing this, but I haven’t found any thorough discussions of the problems that arise.
Let’s take a look at exceptions. Python is excellent at throwing
exceptions, and errs on the side of more exceptions rather than
less. For example, this code will raise KeyError
:
The equivalent code in other languages will just return a sentinel value. For example, JavaScript:
The main disadvantage of the JavaScript approach is that it’s easy to
get confused when you have your sentinel value in the hashmap. It’s
hard to distinguish {}
from {'foobar': undefined}
.
So, there are a large number of situations that will throw exceptions in Python.
Let’s consider the following piece of Django code, which fetches a page, if it exists.
This works as expected. However, can you see the mistake in this version?
This code will actually throw AttributeError
, since Page.object
doesn’t exist. However, since we used a catch-all except
, we don’t
get any error. Subtle bugs like these can be a pain to track down
later.
The solution is to always name the specific exception you’re expecting. Note there’s a nasty gotcha with catching multiple exceptions:
The correct way to write this is:
To avoid this gotcha completely, you can either use the excellent
Pyflakes to check your code,
or use the as
syntax:
As with every rule, there are a few exceptions. The first is when you’re allowing exceptions to propagate.
The other case is long running process, such as daemons:
In this case, you only want a naked except at the very top level of your program, and you want the exception to go somewhere. I like to log to Sentry or to a log file monitored by Papertrail.
To sum up: Only catch the exceptions you’re expecting to see. For all other exceptions, you want to be informed about it so you can fix it.