Re: try: execpt:/ try: finally: behavior

Mark S. Riggle (sasmsr@zinfande.unx.sas.com)
Tue, 21 Jun 1994 20:27:33 GMT

In article <9406211726.AA28146=guido@voorn.cwi.nl>, Guido.van.Rossum@cwi.nl writes:
|> > The scope of exception clauses does not seem to be adjusted during
|> > exception handling that is caught by a finally clause. If two
|> > exception clauses are nested and an execption is raised to the
|> > outermost one and a finally clause is triggered that raises an
|> > exception to the inner one, the outer is lost.
|> [example]
|>
|> This is intended behavior. If a finally clause does anything else
|> than reach its end without problems, that action takes precedence and
|> completely masks the exception (or non-exception) that was held.

If this was really the intended behavior as opposed to incomplete
design (none of us are ever guilty of that are we?), I think the
choice was incorrect. See the example REACTOR.

|> In this way, if the exception raised by the finally clause was caused
|> by a programming error, and presuming it is not masked in turn by a
|> too generous outer except clause, the point of the error in the
|> finally clause shows up in the stack trace, as it should.
|>

I do not quite understand what you mean here. If the scope of an
exception that is raised inside a finally clause is in a more "outer"
scope than the current exception, that should be legal. If it has two
exception clauses for that exception, one inside the current exception
clause and one outside of it, the inner one should be discarded and
the outer one handles it. If this is not clear, another example might
help.

|> I stole this idea from Modula-3, like
|> most of the exception stuff in Python. As far as I can tell M3 has
|> the same semantics as Python in the situation you describe.

I certainly believe that M3 has the same behavior, early lisps did
too. The current implementation is easier to do compared to what I
propose. So unless this scenario was thought of during the design of
the exception stuff, the easier route would be natural.

|>
|> Finally note that the source of your conclusion may be a different
|> model of how exceptions work. The raised exception is not "directed"
|> at the outer except clause -- it is simply an alternate return from
|> the function, which percolates through the stack until a matching
|> exception handler is found. When a finally clause is executed, the
|> pending exception is temporarily saved, and when the finally clause
|> completes normally the saved exception is re-raised.

I don't think I have a different model of exceptions. We disagree on the
extent of an exception handler. That difficulty allows a finally clause
to step in on an exception and decide to pass control to a more RECENTLY
nested handler, thus ignoring the orginal exception.

My previous example recast to show the serious side effects that
can happen.

reactor_overheat= 'reactor_overheat'

def jjj(x):
try: # - outer exception to protect world
try:
jjk(x)
except ZeroDivisionError, val: # - inner error to protect minor errors
print 'execption ZeroDivisionError raised', val
print 'If the reactor_overheats, we are in deep trouble'
except reactor_overheat, val:
print 'Drop the Control Rods QUICK', val

def jjk(x):
try:
print "about to make reactor_overheat"
raise reactor_overheat, "reactor_overheat"
finally:
print "average lifespan around reactor ", 70/x

>>> jjj(3) # - do not trigger the ZeroDivisionError
about to make reactor_overheat
average lifespan around reactor 23
Drop the Control Rods QUICK reactor_overheat

>>> jjj(0) # - trigger the ZeroDivisionError
about to make reactor_overheat
average lifespan around reactor execption ZeroDivisionError raised integer division or modulo
If the reactor_overheats, we are in deep trouble
>>>

So in the jjj(0) call, the reactor overheating exception is lost
(and the world is doomed of course).

Note that the reverse ordering on the jjk function does work correctly
for both our interpretations of how exceptions should be processed.
Ie. if jjk was defined as

def jjk(x):
try:
print "average lifespan around reactor ", 70/x
finally:
print "about to make reactor_overheat"
raise reactor_overheat, "reactor_overheat"

we then get-

>>> jjj(0)
average lifespan around reactor about to make reactor_overheat
Drop the Control Rods QUICK reactor_overheat

The finally clause raises an exception that is outside (nested) of the
divide by zero exception.

Anyone other than Guido and I interested in this issue?

-- 
=========================================================
Mark Riggle                         | "Give me LAMBDA or
sasmsr@unx.sas.com                  |  give me death"
SAS Institute Inc.,                 |
SAS Campus Drive, Cary, NC, 27513   |  
(919) 677-8000                      |