I'm a little confused why objects
are not deleted after they go
out of scope due to an exception?
For e.g.
>>> import time
>>>
>>> def f():
>>> myfile=open("file.test","w")
>>> myfile.write("not flushed\n")
>>> exception=throw
>>>
>>> f()
>>> time.sleep(10)
The file is not written/closed until
the python interpreter exits.
The same thing applies to other objects.
cheers,
Pádraig.
Because objects don't go out of scope. Only variables do. Objects remain
"alive" as long as there are any references to them.
>
> For e.g.
>
> >>> import time
> >>>
> >>> def f():
> >>> myfile=open("file.test","w")
> >>> myfile.write("not flushed\n")
> >>> exception=throw
> >>>
> >>> f()
> >>> time.sleep(10)
>
>
> The file is not written/closed until
> the python interpreter exits.
> The same thing applies to other objects.
In this case, the traceback still holds a reference to the frame from
which the exception was raised, which itself holds a reference to all the
locales from that function.
Calling sys.exc_clear() (possibly followed by gc.collect()) should force
the cleanup you expect.
Jp
OK I can see that, but why doesn't a pass on the exception release it?
This is demonstrated with:
#!/usr/bin/env python
import time
class c:
def __del__(self):
print "del"
def f():
C=c()
exception=throw
try:
f()
except:
pass
time.sleep(3)
> which itself holds a reference to all the
> locales from that function.
don't know what you mean by this
>
> Calling sys.exc_clear()
This isn't in version 2.2.2 at least
> (possibly followed by gc.collect()) should force
> the cleanup you expect.
thanks,
Pádraig.
OK I can see that, but why doesn't a pass on the exception release it?
This is demonstrated with:
#!/usr/bin/env python
import time
class c:
def __del__(self):
print "del"
def f():
C=c()
exception=throw
try:
f()
except:
pass
time.sleep(3)
> which itself holds a reference to all the
> locales from that function.
don't know what you mean by this
>
> Calling sys.exc_clear()
This isn't in version 2.2.2 at least
> (possibly followed by gc.collect()) should force
> the cleanup you expect.
thanks,
Pádraig.
I tried out your recipe, but with no luck:
Python 2.3.2 (#1, Oct 21 2003, 10:03:19)
[GCC 3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class T:
... def __del__(self):
... print "now i'm gone"
...
>>> def f():
... t = T()
... raise Exception
...
>>> import sys, gc
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in f
Exception
>>> sys.exc_clear()
>>> gc.collect()
0
The only way to clear the reference I've found so far is a bit unorthodox:
>>> raise Exception
now i'm gone
Traceback (most recent call last):
File "<stdin>", line 1, in ?
Exception
There seems to be some dark corner of the exception infrastructure that
exc_clear() doesn't touch.
However, I think it's about time to direct the OP to the solution of the
"real" problem, i. e. ensuring that a resource is released when an
exception occurs:
>>> def g():
... t = T()
... try:
... raise Exception
... finally:
... del t
...
>>> g()
now i'm gone
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 4, in g
Exception
>>>
Whether immediate garbage collection occurs, is an implementation detail.
The code will be more portable if try...finally is used in such cases.
Peter
Because they don't go out of scope then -- named locals are still accessible by crawling over the traceback information available via the sys module.
> For e.g.
>
> >>> import time
> >>>
> >>> def f():
> >>> myfile=open("file.test","w")
> >>> myfile.write("not flushed\n")
> >>> exception=throw
> >>>
> >>> f()
> >>> time.sleep(10)
At this point you can still get at myfile, because of the traceback info that still exists. You neglected to show us the
NameError: global name 'throw' is not defined
traceback produced if you actually run this. It's possible to recreate the entire call stack at the point of the exception from the traceback info, and from the call stack you can get to all the locals of all the functions on the call stack, so "myfile" is still reachable. See the docs for sys.exc_info().
Note that locals in Python have lexical scope but dynamic extent: this isn't like C or C++ in the latter respect. All objects stay alive in Python for as long as they're reachable by any means, and nothing is destroyed *just* because a function exits (not even the stack frame is necessarily destroyed then -- it *usually* is, but a traceback can keep it alive, and a generator "yield" statement keeps the frame alive deliberately).
It's probably just an artifact of running in interactive mode. Then you
also have to worry about sys.last_traceback (see the sys docs).
>>> class T:
... def __del__(self):
... print "i'm gone"
...
>>> def f():
... t = T()
... raise Exception
...
>>> import sys
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in f
Exception
>>> sys.last_traceback = None
i'm gone
>>>
You're right. No dark corners then...
Peter