re-entering in the normal flow after an exception is raised

Shai Berger shai at platonix.com
Thu Sep 30 03:11:30 EDT 2004


It's called "restarts" in Common Lisp. The basic idea, if I got it right, is 
that you keep a global mapping from exception types to functions, and 
whenever an exception is raised by the code, if the mapping has an entry for 
it, the function is called instead. Lisp makes it easy to manage the mapping 
in concert with program flow (that is, adding an entry works much like adding 
an exception handler in Python). It is as if you could write,

# PSEUDO-CODE
def ignoreException(exc): continue

try:
 func()
restart NotSeriousException:
 ignoreException

Now, notice that the code under the restart clause returns a function; this is 
the function that will be called instead of raising the exception, within 
do_this, and whatever it returns is returned instead of continuing the 
do_this code.

Common Lisp goes further, and uses this mechanism for error handling that is 
more flexible than is usually achieved with Exceptions, by doing something 
that is essentially like throwing exceptions from the functions used for 
restarts; this means you can have

class SomeError(Exception): pass
class RecoveryStrategy1(Exception): pass
class RecoveryStrategy2(Exception): pass

def innermost():
 raise SomeError()

def medium():
 try:
  innermost()
 except RecoveryStrategy1:
  do_this()
 except RecoveryStrategy2:
  do_that()

def outer():
 try:
  medium()
 restart SomeError:
  def f(exc): raise RecoveryStrategy1()
  f

Which means that medium defines and implements the recovery strategies, but 
only outer chooses among them.

Now, if you don't need the dynamic-try-like feature, I guess you could write 
something like this (untested):

# A special method for raising exceptions
def raise_(exc):
 if exc.__class__.hasattr('restart_func'):
  return exc.__class__.restart_func(exc)
 else:
  raise exc

# Modified from the original to use the new raise_() instead of raise
def do_this():
 raise_(NotSeriousException())

def do_that():
 pass

def do_this_and_that():
 do_this()
 do_that()

def ignore_exception(exc): pass

def call_do_this_and_that():
 #instead of your "try'
 NotSeriousException.restart_func = ignore_exception
 do_this_and_that()

On second thought, this is a lot like Bengt's suggestion of complain().

BTW: We could implement behavior that mimics Common Lisp more closely, if we 
had dicts we could play with on frame objects. We could do plenty of other 
useful stuff, too, but that's for another post.
 



More information about the Python-list mailing list