Re-raise in absence of an "active" exception

http://python.org/sf/973103 points to two interesting bugs in Python: First, using a re-raise after the except-block has completed will still re-raise the last exception. Even though the language spec is ambiguous (what is the "last expression that was active in the current scope" (*)), I doubt this is intended. It then also shows that the error you "normally" get for a reraise in absence of an exception is TypeError: exceptions must be classes, instances, or strings (deprecated), not NoneType I think this is in violation of the language description, which says "If no exception is active in the current scope, an exception is raised indicating this error." "This" error probably being "no active exception", not "exception must not be NoneType". What do you think? Regards, Martin

http://python.org/sf/973103 points to two interesting bugs in Python:
First, using a re-raise after the except-block has completed will still re-raise the last exception. Even though the language spec is ambiguous (what is the "last expression that was active in the current scope" (*)), I doubt this is intended.
Actually, it *is* intended. The exception state remains valid until another exception is raised in the same or an outer scope.
It then also shows that the error you "normally" get for a reraise in absence of an exception is
TypeError: exceptions must be classes, instances, or strings (deprecated), not NoneType
I think this is in violation of the language description, which says
"If no exception is active in the current scope, an exception is raised indicating this error."
"This" error probably being "no active exception", not "exception must not be NoneType".
What do you think?
Here I agree -- the error message is a little dumb. --Guido van Rossum (home page: http://www.python.org/~guido/)

On Mon, 2004-06-21 at 01:22, "Martin v. Löwis" wrote:
"If no exception is active in the current scope, an exception is raised indicating this error."
"This" error probably being "no active exception", not "exception must not be NoneType".
We can determine statically whether an exception would active in the current scope, right? If the raise does not occur within an except handler, then there is no active exception in the current scope. I think it should be a SyntaxError. Jeremy

At 11:15 AM 6/24/04 -0400, Jeremy Hylton wrote:
On Mon, 2004-06-21 at 01:22, "Martin v. Löwis" wrote:
"If no exception is active in the current scope, an exception is raised indicating this error."
"This" error probably being "no active exception", not "exception must not be NoneType".
We can determine statically whether an exception would active in the current scope, right? If the raise does not occur within an except handler, then there is no active exception in the current scope. I think it should be a SyntaxError.
Wouldn't that break something like this: def errorHandler(value): if isinstance(value,SomethingInParticular): # do something else: raise try: # something except Exception,v: errorHandler(v)

On 2004 Jun 24, at 17:15, Jeremy Hylton wrote:
On Mon, 2004-06-21 at 01:22, "Martin v. Löwis" wrote:
"If no exception is active in the current scope, an exception is raised indicating this error."
"This" error probably being "no active exception", not "exception must not be NoneType".
We can determine statically whether an exception would active in the current scope, right? If the raise does not occur within an except handler, then there is no active exception in the current scope. I think it should be a SyntaxError.
Isn't the "raise" allowed to occur in a function that may be _called from_ an except handler? E.g.:
def foo(): ... print "do something here" ... raise ... try: 1/0 ... except Exception: foo()
as one might obtain from refactoring otherwise duplicated "clean up a few things after an exception, then propagate the exception" code into a function foo. Of course, this could easily be coded otherwise (just a bit more verbosely: keep the raise out of foo, use "foo(); raise" wherever foo is now called), but before making a SyntaxError out of a construct that's long been quite correct and may well be present in good working code one would surely need at least one transition release where the construct produces a warning, right? Indeed. I looked for examples in the Python standard library and it wasn't hard to find some (there may be others, I only did a quick rough search) -- compiler/pycodegen.py method visitSlice of class CodeGenerator has a bare raise as the very last line of code (NOT statically inside an except handler); idlelib/rpc.py has a really interesting construct in method handle_error of class RPCServer: try: raise except SystemExit: raise except: erf = sys.__stderr__ (etc etc) also found elsewhere in idlelib, and another simpler bare raise is also in handle_error in class MyMixinServer in test_socketserver.py (indeed such handle_error methods might seem good candidates for cases of "bare raise not statically within an except handler"). It appears to me that finding examples of use of a construct inside such "hallowed" code as found in the standard library further militates in favour of having to be very cautious before making that construct into a SyntaxError. Alex

On Fri, 2004-06-25 at 05:20, Alex Martelli wrote:
On 2004 Jun 24, at 17:15, Jeremy Hylton wrote:
On Mon, 2004-06-21 at 01:22, "Martin v. Löwis" wrote:
"If no exception is active in the current scope, an exception is raised indicating this error."
"This" error probably being "no active exception", not "exception must not be NoneType".
We can determine statically whether an exception would active in the current scope, right? If the raise does not occur within an except handler, then there is no active exception in the current scope. I think it should be a SyntaxError.
Isn't the "raise" allowed to occur in a function that may be _called from_ an except handler? E.g.:
def foo(): ... print "do something here" ... raise ... try: 1/0 ... except Exception: foo()
The definition of what it means for an exception to be "active" in a scope needs to be clarified. The language reference doesn't appear to define what that means, so I took a narrow reading. I think the code you mention later in your post is code I wrote :-). Jeremy

Jeremy Hylton wrote:
The definition of what it means for an exception to be "active" in a scope needs to be clarified. The language reference doesn't appear to define what that means, so I took a narrow reading. I think the code you mention later in your post is code I wrote :-).
It seems that Guido has attempted to clarify it as "an exception is active until the next exception is raised, or the program terminates". However, that clarification doesn't describe the current implementation, atleast not for interactive mode:
try: ... 1/0 ... except: ... pass ... raise Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: exceptions must be classes, instances, or strings (deprecated), not NoneType
Here, the re-raise doesn't find an exception anymore, even though non has been raised... I would prefer if it were clarified as "An exception is active until the exception handler completes. When control runs off the end of the exception handler, or it completes through a return, break, or continue, no exception is active. When a new exception is raised (or the currently active one is re-raised), that exception becomes active." Regards, Martin

At 01:02 PM 6/26/04 +0200, Martin v. Löwis wrote:
It seems that Guido has attempted to clarify it as "an exception is active until the next exception is raised, or the program terminates".
However, that clarification doesn't describe the current implementation, atleast not for interactive mode:
try: .. 1/0 .. except: .. pass .. raise Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: exceptions must be classes, instances, or strings (deprecated), not NoneType
Here, the re-raise doesn't find an exception anymore, even though non has been raised...
Isn't each statement/block in the interactive interpreter run in a new frame?

Phillip J. Eby wrote:
try: .. 1/0 .. except: .. pass .. raise Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: exceptions must be classes, instances, or strings (deprecated), not NoneType
Here, the re-raise doesn't find an exception anymore, even though non has been raised...
Isn't each statement/block in the interactive interpreter run in a new frame?
Yes, but why does that matter? If the language spec says "the exception stays until the next exception is raised", then the interpreter doesn't conform to the language spec, and has a bug. Regards, Martin

At 02:20 PM 6/26/04 +0200, Martin v. Löwis wrote:
Phillip J. Eby wrote:
try: .. 1/0 .. except: .. pass .. raise Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: exceptions must be classes, instances, or strings (deprecated), not NoneType
Here, the re-raise doesn't find an exception anymore, even though non has been raised...
Isn't each statement/block in the interactive interpreter run in a new frame?
Yes, but why does that matter? If the language spec says "the exception stays until the next exception is raised", then the interpreter doesn't conform to the language spec, and has a bug.
I thought the spec was that exceptions are active for the life of a frame's execution. That's probably my projection of the implementation onto an ambiguous phrase in the spec, though. Nonetheless, I think that the ambiguity can and should be resolved by defining "active in the current scope" to mean, "caught by an exception handler in the current frame or one of its parents". This explanation should probably be added to the section on "Exceptions", and cross-referenced from the section on "raise". Is there consensus such that I should make these changes to the spec?

Phillip J. Eby wrote:
Is there consensus such that I should make these changes to the spec?
Well, no - I still think it is a bug that re-raising after then end of the exception handler but before the end of the function "works". I'm actually surprised that the exception is cleared when the function that has the exception handler completes. Where does that happen? The current exception is in the thread state, not in the frame, after all. But then, consensus on the semantics isn't needed, as you should be looking for BDFL pronouncement. So you might be asking "Is there consensus that these changes reflect the BDFL pronouncement?" to which I can't respond, as I haven't understood the BDFL pronouncement. Regards, Martin

Hello, Sorry, I only replied to the SF tracker. The language specs are vague but a very precise explanation for the "currently active exception" is given in the library reference for sys.get_info(). The language spec can be made precise just by copying or pointing to that text. In other words, if we make explicit the implicit assumption that a bare 'raise' is indeed intended equivalent to exc, val, tb = sys.exc_info() raise exc, val, tb then the docs are already precise enough and CPython really implements that. A bientôt, Armin.

On Sun, 2004-06-27 at 07:39, Armin Rigo wrote:
In other words, if we make explicit the implicit assumption that a bare 'raise' is indeed intended equivalent to
exc, val, tb = sys.exc_info() raise exc, val, tb
then the docs are already precise enough and CPython really implements that.
IIRC the discusions at the time bare raise was added, those were exactly the intended semantics. -Barry

I'm actually surprised that the exception is cleared when the function that has the exception handler completes.
I think it's probably when the "except" clause of the exception handler completes, not the function. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

Greg Ewing wrote:
I'm actually surprised that the exception is cleared when the function that has the exception handler completes.
I think it's probably when the "except" clause of the exception handler completes, not the function.
See for yourself: def f(): try: raise Exception, "Hello" except: pass raise f() gives Traceback (most recent call last): File "c.py", line 8, in ? f() File "c.py", line 3, in f raise Exception, "Hello" Exception: Hello Regards, Martin
participants (9)
-
"Martin v. Löwis"
-
Alex Martelli
-
Armin Rigo
-
Barry Warsaw
-
Greg Ewing
-
Guido van Rossum
-
Jeremy Hylton
-
Jeremy Hylton
-
Phillip J. Eby