The bare except
statement is something I often see in the main loop of a
script that should run indefinitely. Pylint warns you about this problem
(W0702: No exception type(s) specified (bare-except)
), but in my opinion we
should start to treat this as an error like flake8 does (E722 do not use bare
'except'
).
Consider the following code snippet:
This will work nicely to keep your program running whatever happends inside you
risky_business
function. But let’s try to stop your program by pressing
CTRL-C.
$ python a.py
^C('Problems?', (<type 'exceptions.KeyboardInterrupt'>, KeyboardInterrupt(), <traceback object at 0x7f234f6c9dd0>))
We caught the KeyboardInterrupt
exception and just carried on. But we catch
more which we should not. Let’s try to exit the program from the business
function, maybe because the user asked for it or we got signaled.
$ python a.py
('Problems?', (<type 'exceptions.SystemExit'>, SystemExit(0,), <traceback object at 0x7f49b7ab5e18>))
('Problems?', (<type 'exceptions.SystemExit'>, SystemExit(0,), <traceback object at 0x7f49b7ab5ea8>))
...
So why is this happening? Both KeyboardInterrupt
and SystemExit
are
exceptions (derived from BaseException
). An except
statement without any
restriction will catch any exception derived from BaseException
. Here is the
complete hierachy of the builtin
exceptions.
We can fix our problem by restricting the kind of exception that we want to catch:
When we press CTRL-C now, the program exits with the right exception.
$ python a.py
^CTraceback (most recent call last):
File "a.py", line 10, in <module>
risky_business()
File "a.py", line 6, in risky_business
time.sleep(1)
KeyboardInterrupt
Pylint will still warn you about this line, because catching Exception
is
still very broad and you should only do this when you have a good reason, e.g.
the reason mentioned above (to keep a service running even if it could not
handle a certain input or request).
So next time you see a bare except
statement in your code, take a moment to
reconsider if you really want to catch SystemExit
, KeyboardInterrupt
(and
GeneratorExit
which I did not mention above). If that is the case, you might
want to make it explicit so that the next person reading the code does not have
to check again.