expect(gc.collect(), 4, "boom2")
expect(len(gc.garbage), garbagelen, "boom2")
+# boom__new and boom2_new are exactly like boom and boom2, except use
+# new-style classes.
+
+class Boom_New(object):
+ def __getattr__(self, someattribute):
+ del self.attr
+ raise AttributeError
+
+def test_boom_new():
+ a = Boom_New()
+ b = Boom_New()
+ a.attr = b
+ b.attr = a
+
+ gc.collect()
+ garbagelen = len(gc.garbage)
+ del a, b
+ expect(gc.collect(), 4, "boom_new")
+ expect(len(gc.garbage), garbagelen, "boom_new")
+
+class Boom2_New(object):
+ def __init__(self):
+ self.x = 0
+
+ def __getattr__(self, someattribute):
+ self.x += 1
+ if self.x > 1:
+ del self.attr
+ raise AttributeError
+
+def test_boom2_new():
+ a = Boom2_New()
+ b = Boom2_New()
+ a.attr = b
+ b.attr = a
+
+ gc.collect()
+ garbagelen = len(gc.garbage)
+ del a, b
+ expect(gc.collect(), 4, "boom2_new")
+ expect(len(gc.garbage), garbagelen, "boom2_new")
+
def test_all():
gc.collect() # Delete 2nd generation garbage
run_test("lists", test_list)
run_test("trashcan", test_trashcan)
run_test("boom", test_boom)
run_test("boom2", test_boom2)
+ run_test("boom_new", test_boom_new)
+ run_test("boom2_new", test_boom2_new)
def test():
if verbose:
Release date: XX-XXX-2003
============================
+- Some horridly obscure problems were fixed involving interaction
+ between garbage collection and classes with "ambitious" getattr hooks.
+ If a class instance didn't have a __del__ method, but did have a
+ __getattr__ hook, and the instance became reachable only from an
+ unreachable cycle, and the hook resurrected or deleted unreachable
+ objects when asked to resolve "__del__", anything up to a segfault
+ could happen. That's been repaired.
+
- Skip locale test on Mac OS X.
- Backported the "largefile" requirement for test_largefile on Mac OS X.
* could call the class's __getattr__ hook (if any). That could invoke
* arbitrary Python code, mutating the object graph in arbitrary ways, and
* that was the source of some excruciatingly subtle bugs.
- * XXX This is still broken for new-style classes.
*/
static int
has_finalizer(PyObject *op)
if (PyInstance_Check(op))
return _PyInstance_Lookup(op, delstr) != NULL;
else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE))
- /* XXX This path is still Evil. */
- return PyObject_HasAttr(op, delstr);
+ return _PyType_Lookup(op->ob_type, delstr) != NULL;
else
return 0;
}
static void
move_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
{
- PyGC_Head *next;
PyGC_Head *gc = unreachable->gc.gc_next;
- for (; gc != unreachable; gc=next) {
+
+ while (gc != unreachable) {
+ PyGC_Head *next = gc->gc.gc_next;
PyObject *op = FROM_GC(gc);
- /* XXX has_finalizer() may result in arbitrary Python
- code being run. */
+
if (has_finalizer(op)) {
- next = gc->gc.gc_next;
gc_list_remove(gc);
gc_list_append(gc, finalizers);
gc->gc.gc_refs = GC_MOVED;
}
- else
- next = gc->gc.gc_next;
+ gc = next;
}
}