FYI, an exception-handling trick

Tim Peters (tim@ksr.com)
Mon, 02 Mar 92 02:56:52 EST

Maybe this is obvious to y'all, and apologies in advance if so. Wasn't
obvious to me, and it seems like a pretty slick trick, so ...

When writing a function that doesn't (or can't) trust its caller to have
passed good stuff-- when they may have passed something that will cause
an unpredictable exception, and it's not easy to check that in advance
--I've been handling it like this:

try:
<dangerous code>
except: # catch every possible goof
print 'Unhappy with what you passed!'
print 'But it\'s not my fault, it\'s yours!'
print 'Wish I could tell you more, but I\'m too lazy to write'
print 'an "except" clause for every possible problem.'
raise UnhappyError

What I really want here is some way to pass the original exception up to
the caller, but to print my own msg first so that the user doesn't
assume that the problem was caused by my code <grin>.

This seems to do the trick, & quite pleasantly:

bad_user = 1 # assume they screwed up
try:
<dangerous code>
bad_user = 0 # guess they didn't -- this time
finally:
if bad_user: print 'Bad user!'

If nothing goes wrong, bad_user is set to 0 and so the 'finally' block
just falls through. If something does go wrong in '<dangerous code>',
Python aborts executing the 'try' block, so bad_user does not get
zeroed, so the 'finally' block prints 'Bad user!', and the exception is
passed up.

example-attached-ly y'rs - tim

Tim Peters Kendall Square Research Corp
tim@ksr.com, ksr!tim@uunet.uu.net

This is module example.py:

import regexp

lambda_recognizer = \
regexp.compile( '^[ \t]*lambda[ \t]*\(' ).match

def map( l, f ):
if type(f) is type(''):

try: paren_index = lambda_recognizer(f)[0][1] - 1
except:
print 'Error in map, arg 2: string not a lambda'
raise ValueError, `f`

f = 'def f' + f[paren_index:] # 'lambda' -> 'def f'
bad = 1 # guilty until proven innocent
try:
exec( f + '\n' ) # creates local func 'f'
bad = 0
finally:
if bad:
print 'Error in map, arg 2: ' + \
'couldn\'t exec ' + `f`

ans = []
for elt in l: ans.append( f(elt) )
return ans

And a short session illustrating it:

>>> from example import map
>>> a = [1, 2, 8]
>>> def p1(n): return n+1
...
>>> map( a, p1 )
[2, 3, 9]
>>> map( a, 'lambda(n): return n*n' )
[1, 4, 64]
>>> map( a, 'lamda(n): return n*n' )
Error in map, arg 2: string not a lambda
Unhandled exception: ValueError: 'lamda(n): return n*n'
Stack backtrace (innermost last):
File "<stdin>", line 1
File "./example.py", line 12
raise ValueError, `f`
>>> map( a, 'lambda(n): return n*' )
Error in map, arg 2: couldn't exec 'def f(n): return n*'
Unhandled exception: SyntaxError: invalid syntax
Stack backtrace (innermost last):
File "<stdin>", line 1
File "./example.py", line 17
exec( f + '\n' ) # creates local func 'f'
>>> END OF MSG