Exception feature creep! (was: re-entering in the normal flow after an exception is raised)

Lonnie Princehouse finite.automaton at gmail.com
Fri Oct 1 21:52:38 CEST 2004


In a recent post, Michele Simionato asked about resumable (or
re-entrant) exceptions, the basic idea being that when raised and
uncaught, these exceptions would only propagate backwards through a
limited number of frames before being implicitly ignored.  Alex
Martelli pointed out that resumable exceptions are difficult to
implement, and that the need for them isn't pressing because it's
pretty easy to write a program with explicit error handling that works
just fine, citing Bjarne Stroustrup's thoughts on the same topic for
C++.
This is probably quite right.

Nonetheless, I tried to write some demo code that implemented
resumable exceptions, but ran face-first into a brick wall:  I
couldn't find any way to manipulate the call stack (to execute code in
a frame other than the current one)

What if exceptions were given more control?

Proposal: 

1. Call a __raise__ method in the exception when raised
2. Call an __uncaught__ method in every frame for which the exception
is not handled by an except clause.   __uncaught__ would decide
whether or not to continue to propagate the exception to the next
frame.  If __uncaught__ decides to abort the exception, then the
calling statement is aborted/ignored--- the implicit equivalent of:

try:
  do_something()
except:
  pass


Anyway, this is just idle speculation.  I don't really see a need for
it, but it's interesting to ponder.


Here are some hypothetical uses:


1. An exception that propagates only n frames:

class QuietException(Exception):
  def __init__(self, n):
    Exception.__init__(self)
    self.n = n

  def __raise__(self):
    self.counter = self.n

  def __uncaught__(self):
    if self.counter > 0:
      self.counter -= 1
      return True
    else:
      return False

2. An exception that keeps a traceback as it goes:
 
class TracingException(Exception):

  def __raise__(self):
    self.traceback = []

  def __uncaught__(self):
    self.traceback.append(sys._getframe(1))

...

try: 
  foo()
except TracingException, e:
  # isn't this more elegant than sys.last_traceback?
  import traceback
  traceback.print_tb(e.traceback)


3. An exception that resumes execution if some global FAULT_TOLERANT
is true:

class FaultTolerantException(Exception):
  def __uncaught__(self):
    return not FAULT_TOLERANT



More information about the Python-list mailing list