Subject: Forcing exit(); unqualified exception catchers - DN [1]


Edward Welbourne <eddyw@lsl.co.uk> - 21 Jan 2000 - comp.lang.python

 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:
     during this operation, do I really want to ignore the user's request ?
     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.

Last modified
2000-02-10

(195.108.246.50)

Note: you are looking at
the snapshot of an old wiki
- much of this information
is likely to be very outdated