Why can't I CTRL-C out of this script? Oh:
while True:
try:
x = do_the_thing()
return x
except:
log('An error occurred, retry')
This Code Kills article gives a few good suggestions on how to avoid this.
One case that I've encountered a few times is this: I need to call some function that might fail. I don't particularly care how or why it failed, but I want to raise a new exception type, because higher level code only cares about what operation was attempted, not how it failed.
So I write something like this:
def print_document(doc, printer):
try:
conn = open_connection_to_printer(printer)
conn.send_print_job(doc)
except Exception:
raise PrintError
Catching Exception
is significant. This filters out things like KeyboardInterrupt
and SystemExit
, which prevents my original complaint about CTRL-C not working.
Because network connections can raise a lot of different error types, I don't really feel like trying to enumerate all the possible errors (plus, the next version of that module might add new ones).
But then a day comes when I'm sure that the network connection is okay, and I really need to diagnose why this code is failing. I wish I could see what happened at the moment I raised PrintError
.
My improved code looks like this:
def print_document(doc, printer):
try:
conn = open_connection_to_printer(printer)
conn.send_print_job(doc)
except Exception as e:
raise PrintError(err=e), None, sys.exc_info()[2]
That last line contains a bit too much opaque cleverness, but it does two important things:
- It captures the original exception, so even though we propagate
PrintError
upwards, we can always dig into the innererr
as needed (for example,PrintError
's__str__
or__repr__
might print outerr
. - It preserves the original stacktrace. I didn't know I needed this until my code started failing with an unexpected
PrintError
, with the innererr
containing, say,KeyError
. By itself that's not enough information to tell what happened; I want to see where theKeyError
was raised, but in my earlier code the stacktrace ends atprint_document()
. Using the long form ofraise
(reference) allows me to pass the original stacktrace instead.