except statement is something I often see in the main loop of a
script that should run indefinitely. Pylint will
warn you about this
problem, but in my opinion we should start to treat this as an error.
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
$ 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
exceptions (derived from
except statement without any
restriction will catch any exception derived from
BaseException. Here is the
complete hierachy of the builtin
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
So next time you see a bare
except statement in your code, take a moment to
reconsider if you really want to catch
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.