[Python-Dev] Cycle collection enhancement idea

Eyal Lotem eyal.lotem at gmail.com
Sat Jun 28 15:40:04 CEST 2008


I see why a cycle that has multiple objects with a __del__ method is a problem.

Once you call __del__ on one of the objects, its no longer usable by
the others, and its not clear which order is correct.

My question regards the case where a cycle of objects only has 1
object which has a __del__.

I think a correct strategy to collect the entire cycle is the same one
used on a single object.  On a single object Python uses:
1. Temporarily revive object
2. Call __del__
3. Unrevive object (if(--refcount == 0) then we're done), otherwise it
was resurrected).

We can apply this to the whole cycle:
1. Temporarily revive entire cycle (each of its objects)
2. Call __del__
3. Unrevive the objects of the entire cycle (each of its objects).

Step 1 will allow __del__ to run safely. Since there is only one
__del__ in the cycle, it is not dangerous that its references will
disappear from "under its feet".
(Some code restructuring will probably be necessary, because of
assumptions that are hard-coded into slot_tp_del and subtype_dealloc).

I believe this enhancement is important, because:
A. When using existing code -- you do not control whether its objects
have a __del__. In my experience, a majority of these cases only have
a single __del__-containing object in their cycles.
B. Python's exit cleanup calls __del__ in the wrong order, and
Python's runtime is full of cycles (Each global is a cycle, including
the class objects themselves: class->dict->function->func_globals)).
These cycles very often have only 1 __del__ method.

Some examples of the problem posed by B:
http://www.google.com/search?q=ignored+%22%27NoneType%27+object+has+no+attribute%22+%22__del__+of%22&btnG=Search
Ugly workarounds exist even in the standard library [subprocess]: "def
__del__(self, sys=sys):").

Example:

import os
class RunningFile(object):
    filename = '/tmp/running'
    def __init__(self):
        open(self.filename, 'wb')
    def __del__(self):
        os.unlink(self.filename)
running_file = RunningFile()

The deller object is in a cycle as described above [as well as the
Deller class itself].  When Python exits, it could call
deller.__del__() and then collect the cycle. But Python does the wrong
thing here, and gets rid of the globals before calling __del__:
Exception exceptions.AttributeError: "'NoneType' object has no
attribute 'unlink'" in <bound method RunningFile.__del__ of
<__main__.RunningFile object at 0x7f9655eb92d0>> ignored

I believe applying the above enhancement would solve these problems.


More information about the Python-Dev mailing list