Destructors and exceptions

Duncan Booth me at privacy.net
Wed Jun 9 17:02:50 CEST 2004


dkturner at telkomsa.net (David Turner) wrote in
news:e251b7ba.0406090616.38e46344 at posting.google.com: 

>> > 
>> > Then why not unreference the traceback (and therefore destroy it
>> > and the down-stack locals, if the exception handler hasn't made
>> > another reference) at the end of the handling suite?
>> 
>> Fine, except that isn't what Python does now, and the current
>> behaviour needs to be kept for compatibility. So if that is the
>> behaviour you want, you have to do that explicitly yourself at the
>> end of every exception handler.
> 
> Are you sure the current behaviour needs to be kept?  Isn't
> referencing the traceback outside of an exception handler a little
> dodgy in the first place?  I'm sorry if I sound argumentative, but I
> do want to understand the issues thoroughly :-).
> 
The documentation says:

> exc_info( ) 
> 
> This function returns a tuple of three values that give information
> about the exception that is currently being handled. The information
> returned is specific both to the current thread and to the current
> stack frame. If the current stack frame is not handling an exception,
> the information is taken from the calling stack frame, or its caller,
> and so on until a stack frame is found that is handling an exception.
> Here, ``handling an exception'' is defined as ``executing or having
> executed an except clause.'' For any stack frame, only information
> about the most recently handled exception is accessible. 

So, in fact I was wrong. The exception gets cleared when the function that 
handled it returns:

>>> import sys
>>> def f():
...    try:
...       raise RuntimeError
...    except:
...       print sys.exc_info()
...    print sys.exc_info()
...
>>> f()
(<class exceptions.RuntimeError at 0x00864810>, <exceptions.RuntimeError 
instance at 0x008EA940>, <traceback object at 0x008EA968>)
(<class exceptions.RuntimeError at 0x00864810>, <exceptions.RuntimeError 
instance at 0x008EA940>, <traceback object at 0x008EA968>)
>>> def g():
...    sys.exc_clear()
...    f()
...    print sys.exc_info()
...
>>> g()
(<class exceptions.RuntimeError at 0x00864810>, <exceptions.RuntimeError 
instance at 0x008EA990>, <traceback object at 0x008EA940>)
(<class exceptions.RuntimeError at 0x00864810>, <exceptions.RuntimeError 
instance at 0x008EA990>, <traceback object at 0x008EA940>)
(None, None, None)
>>>


Evidently Python does clear the exception when you leave the exception 
handler, it is just that the exception handler stretches a bit further than 
you might expect. This of course matters more when you put the exception 
handler inside a loop, or do more processing after the handler has caught 
the exception. I'm not sure how much effect it would have to restrict the 
handler to actually inside the except clause.

In fact further investigation shows that the exception context is saved and 
restored across function calls:

>>> def h():
...    try:
...       raise ValueError, 'h'
...    except:
...       print sys.exc_info()
...    f()
...    print sys.exc_info()
...
>>> h()
(<class exceptions.ValueError at 0x00864DB0>, <exceptions.ValueError 
instance at 0x008EAAA8>, <traceback object at 0x008EA940>)
(<class exceptions.RuntimeError at 0x00864810>, <exceptions.RuntimeError 
instance at 0x008EA760>, <traceback object at 0x008EAAD0>)
(<class exceptions.RuntimeError at 0x00864810>, <exceptions.RuntimeError 
instance at 0x008EA760>, <traceback object at 0x008EAAD0>)
(<class exceptions.ValueError at 0x00864DB0>, <exceptions.ValueError 
instance at 0x008EAAA8>, <traceback object at 0x008EA940>)
>>>

I never knew it did that.



More information about the Python-list mailing list