When is bare except: justified?

John J. Lee jjl at pobox.com
Fri Dec 5 18:13:50 CET 2003


Bare "except:", with no exception specified, is nasty because it can
easily hide bugs.

There is a case where it seemed useful to me in the past.  Now it
seems like a bad idea to me (but I think I might still be confused
about it, hence this post).  It's this: When you're processing a input
from a file-like object connected to an external source like a network
connection, disk file etc, you have to expect broken, and often
malicious, data (especially from data that arrived from the internet,
of course).  except: seemed to me like a good way of dealing with data
that tripped up tacit assumptions in my code about the data.

First example: I have a method that reads cookie data from an HTTP
response and processes it.  If the data is malformed in a way I didn't
anticipate, it seems like it would be nice if the code ignored it
rather than crashing the application using my library, or forcing the
application to halt all processing just because of one little bit of
bad data.  Still, if you take the attitude that malformed data should
be explicitly detected by the code (whether by catching an exception
or doing a direct "look before you leap" test), rather than relying on
unexpected exceptions, this technique could be seen as hiding a bug
every time it successfully does its job!  So I suppose I should just
let the exception propagate, regard any exceptions that show up as
bugs, and fix them.  It is of course often useful to let exceptions do
the work, reducing lines of code and avoiding race conditions by not
explicitly checking things.  But, even when faced with malicious
network data, I suppose that's only justified when you understand with
reasonable specificity which exceptions are going to be raised and for
what reason -- and my use of except: definitely does not fall into
that category.

Even more doubtfully, I have a pair of methods .save() and .load().
.save() is straightforward: it writes the object's state to a disk
file.  .load() loads new data from a disk file, *appending* to the
data held by the object.  .load() is supposed to raise IOError when
something goes wrong (IOError is probably the wrong class here, but
that's another issue).  .load() makes sure it only raises that error
by catching everything then re-raising IOError: I do this like so:

try:
    self.load_data()
except:
    reraise_unmasked_exceptions((IOError,))
    raise IOError("invalid file format")

def reraise_unmasked_exceptions(unmasked=()):
    # There are a few catch-all except: statements in this module, for
    # catching input that's bad in unexpected ways.
    # This function re-raises some exceptions we don't want to trap.
    if ClientCookie.CLIENTCOOKIE_DEBUG:
        raise
    unmasked = unmasked + (KeyboardInterrupt, SystemExit)
    etype = sys.exc_info()[0]
    if issubclass(etype, unmasked):
        raise

The network data case uses the same function, but like so:

try:
    cookies = self.parse_cookies()
except:
    reraise_unmasked_exceptions()
    cookies = []

Problems:

 0. It's ugly and hard to understand.
 1. May mask bugs.
 2. Even when bugs are not masked, it won't be obvious what happened
    without turning on the debug switch.


Part of the reason it's supposed to only raise that error is that
.revert() is used to to *overwrite* (not append) to the data held by
the objecct, implemented something like this:

def revert(self, filename):
    old_state = copy.deepcopy(self.cookies)
    self.cookies = {}
    try:
        self.load(filename)
    except IOError:
        self.cookies = old_state
        raise

But of course the bare except: could equally well be in .revert(), not
.load().  And really, I now think it shouldn't be there at all,
because it can only hide bugs.  Right?

When *is* it justified to use except:, then?

I've seen simple uses like this that seem fine (this one from Alex
Martelli, I think):

def isstringlike(x):
    try: x+""
    except: return False
    else: return True


That's fine because x+"" is too simple to have a bug in it.  Even
there, though, what happens if KeyboardInterrupt happens just as the
try block is getting executed?

Another reasonable use, in a straightforward debugging hack:

try:
    ...
except:
    # use traceback module here to print out some detail of the exception
    ...
    raise


And:

try:
    use_dodgy_plugin_code()
except:
    print "your plugin is buggy"


Anywhere else?


John




More information about the Python-list mailing list