Forcing exit(); unqualified exception catchers

Edward Welbourne eddyw at lsl.co.uk
Fri Jan 21 11:44:35 EST 2000


Note that calling _exit() means any dangling `finally:' clauses will get
skipped.  If these were just waiting to close a few files, no problem;
the system will do so for you.  However, if you had (say) some files
locked by a dumb-but-portable locking scheme that creates a lock-file
you are beholden to tidy away in a finally: clause, you have a problem.
Likewise if you've got any kind of persistent resource claimed.

Most likely, though, your problem is that your code is using unqualified
exception catchers - that is,

try:
    # ...
except:
    # ...

so that your SystemExit exception is getting caught and discarded - as
opposed to

try:
    # ...
except AnticipatedError:
    # ...

Use of unguarded except: is commonly a result of laziness, e.g.

try: 
    n = string.atoi(text)
except:
    n = 0

where someone `knows' that if an exception got raised it can only have
been the ValueError and they know what to do in that case ... except
that if the user tries to interrupt the program (KeyboardInterrupt)
during the execution of string.atoi, the program will ignore the user
and act as though the text had been unreadable.  This trains users to
type ^C^C^C^C^C^C^C^C in frustration, while pieces of your process take
it in turn to do fragments of their jobs, potentially making the mess
worse and worse at each layer ...

That's the noddy case; the more severe cases involve someone not knowing
*which* exception might be coming back from something they're doing, or
maybe there are several possibilities and they can't be bothered to work
out an exhaustive list of them, but again they presume that any
exception raised is `something wrong with the args I passed down' and
they think they know what to do in such a case.  So they just catch all
exceptions and totally ignore the fact that something down under there
might have legitimate grounds for trying to exit (say), or (again) the
user might KeyboardInterrupt.

Note that many programmers believe that the relevant piece of code is so
quick they can rely on the interrupt not happening then.  This is, of
course, a superstition: the interrupt is going to happen somewhen and
the universal law of natural cussedness says the probability of it
happening in any given piece of code is directly proportional to the
amount of grief that will arise if it does - and totally independent of
the length of time the relevant code takes to run.

In general, if you use a unqualified except: clause, ask yourself:
  * if the user tries to interrupt the process, and by ill luck does so
    during this operation, do I really want to ignore the user's request ?
  * if some subordinate operation I haven't anticipated works out that
    it *has* to exit *now*, do I really believe I know better ?

Note that, where several exceptions might get raised, you *can* specify
an except clause that catches them - by using a tuple of exceptions:

try:
    return db.connect(user, password)
except (TypeError, ValueError):
    return None

The other way one *might* approach this, particularly in the case where
many exceptions are possible, would be:

try:
    return db.connect(user, password)
except (KeyboardInterrupt, SystemExit), what:
    raise what
except:
    return None

which will ensure you propagate the ones you realise you don't mean to
lose while catching the rest.  However, you'll have to think long and
hard about your list of which exceptions to forward (e.g. how about
MemoryError ?), lest you go catching something that communicates a state
of affairs that's really so bad you don't want to be responsible for the
aftermath.

So: the real lesson here is, use SystemExit or exit(), not _exit(), and
fix the wanton except: clauses that are preventing your SystemExit from
having its intended effect.  That way, all your finally: clauses will
get to do their stuff.

	Eddy.




More information about the Python-list mailing list