From 7ddda7830c362f59f154681fe9b2aa0c161309b1 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 1 Jan 2009 15:35:33 +0000 Subject: Merged revisions 68128 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68128 | antoine.pitrou | 2009-01-01 15:11:22 +0100 (jeu., 01 janv. 2009) | 3 lines Issue #3680: Reference cycles created through a dict, set or deque iterator did not get collected. ........ --- Lib/test/test_deque.py | 21 +++++++++++++++++-- Lib/test/test_dict.py | 16 +++++++++++++++ Lib/test/test_set.py | 17 +++++++++++++-- Misc/NEWS | 3 +++ Modules/_collectionsmodule.c | 23 ++++++++++++++------- Objects/dictobject.c | 49 +++++++++++++++++++++++++++++--------------- Objects/setobject.c | 16 +++++++++++---- 7 files changed, 114 insertions(+), 31 deletions(-) diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 7af0803..27c89c4 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -1,7 +1,8 @@ from collections import deque import unittest from test import support, seq_tests -from weakref import proxy +import gc +import weakref import copy import pickle from io import StringIO @@ -420,6 +421,22 @@ class TestBasic(unittest.TestCase): d.append(1) gc.collect() + def test_container_iterator(self): + # Bug #3680: tp_traverse was not implemented for deque iterator objects + class C(object): + pass + for i in range(2): + obj = C() + ref = weakref.ref(obj) + if i == 0: + container = deque([obj, 1]) + else: + container = reversed(deque([obj, 1])) + obj.x = iter(container) + del obj, container + gc.collect() + self.assert_(ref() is None, "Cycle was not collected") + class TestVariousIteratorArgs(unittest.TestCase): def test_constructor(self): @@ -530,7 +547,7 @@ class TestSubclass(unittest.TestCase): def test_weakref(self): d = deque('gallahad') - p = proxy(d) + p = weakref.proxy(d) self.assertEqual(str(p), str(d)) d = None self.assertRaises(ReferenceError, str, p) diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 403d5eb..2e74b68 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -2,6 +2,7 @@ import unittest from test import support import sys, collections, random, string +import gc, weakref class DictTest(unittest.TestCase): @@ -648,6 +649,21 @@ class DictTest(unittest.TestCase): pass d = {} + def test_container_iterator(self): + # Bug #3680: tp_traverse was not implemented for dictiter and + # dictview objects. + class C(object): + pass + views = (dict.items, dict.values, dict.keys) + for v in views: + obj = C() + ref = weakref.ref(obj) + container = {obj: 1} + obj.v = v(container) + obj.x = iter(obj.v) + del obj, container + gc.collect() + self.assert_(ref() is None, "Cycle was not collected") from test import mapping_tests diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index da3ed0f..5a8819f 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -1,6 +1,7 @@ import unittest from test import support -from weakref import proxy +import gc +import weakref import operator import copy import pickle @@ -323,6 +324,18 @@ class TestJointOps(unittest.TestCase): self.assertEqual(sum(elem.hash_count for elem in d), n) self.assertEqual(d3, dict.fromkeys(d, 123)) + def test_container_iterator(self): + # Bug #3680: tp_traverse was not implemented for set iterator object + class C(object): + pass + obj = C() + ref = weakref.ref(obj) + container = set([obj, 1]) + obj.x = iter(container) + del obj, container + gc.collect() + self.assert_(ref() is None, "Cycle was not collected") + class TestSet(TestJointOps): thetype = set basetype = set @@ -546,7 +559,7 @@ class TestSet(TestJointOps): def test_weakref(self): s = self.thetype('gallahad') - p = proxy(s) + p = weakref.proxy(s) self.assertEqual(str(p), str(s)) s = None self.assertRaises(ReferenceError, str, p) diff --git a/Misc/NEWS b/Misc/NEWS index d7adf60..62b7a85 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 0 Core and Builtins ----------------- +- Issue #3680: Reference cycles created through a dict, set or deque iterator + did not get collected. + - Issue #4701: PyObject_Hash now implicitly calls PyType_Ready on types where the tp_hash and tp_dict slots are both NULL. diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index d7a6042..910c25f 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -900,7 +900,7 @@ deque_iter(dequeobject *deque) { dequeiterobject *it; - it = PyObject_New(dequeiterobject, &dequeiter_type); + it = PyObject_GC_New(dequeiterobject, &dequeiter_type); if (it == NULL) return NULL; it->b = deque->leftblock; @@ -909,14 +909,22 @@ deque_iter(dequeobject *deque) it->deque = deque; it->state = deque->state; it->counter = deque->len; + _PyObject_GC_TRACK(it); return (PyObject *)it; } +static int +dequeiter_traverse(dequeiterobject *dio, visitproc visit, void *arg) +{ + Py_VISIT(dio->deque); + return 0; +} + static void dequeiter_dealloc(dequeiterobject *dio) { Py_XDECREF(dio->deque); - Py_TYPE(dio)->tp_free(dio); + PyObject_GC_Del(dio); } static PyObject * @@ -981,9 +989,9 @@ static PyTypeObject dequeiter_type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)dequeiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -1002,7 +1010,7 @@ deque_reviter(dequeobject *deque) { dequeiterobject *it; - it = PyObject_New(dequeiterobject, &dequereviter_type); + it = PyObject_GC_New(dequeiterobject, &dequereviter_type); if (it == NULL) return NULL; it->b = deque->rightblock; @@ -1011,6 +1019,7 @@ deque_reviter(dequeobject *deque) it->deque = deque; it->state = deque->state; it->counter = deque->len; + _PyObject_GC_TRACK(it); return (PyObject *)it; } @@ -1063,9 +1072,9 @@ static PyTypeObject dequereviter_type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)dequeiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 8a65b0c..1868466 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2122,7 +2122,7 @@ static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; - di = PyObject_New(dictiterobject, itertype); + di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) return NULL; Py_INCREF(dict); @@ -2139,6 +2139,7 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype) } else di->di_result = NULL; + _PyObject_GC_TRACK(di); return (PyObject *)di; } @@ -2147,7 +2148,15 @@ dictiter_dealloc(dictiterobject *di) { Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); - PyObject_Del(di); + PyObject_GC_Del(di); +} + +static int +dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) +{ + Py_VISIT(di->di_dict); + Py_VISIT(di->di_result); + return 0; } static PyObject * @@ -2228,9 +2237,9 @@ PyTypeObject PyDictIterKey_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -2300,9 +2309,9 @@ PyTypeObject PyDictIterValue_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -2386,9 +2395,9 @@ PyTypeObject PyDictIterItem_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -2415,7 +2424,14 @@ static void dictview_dealloc(dictviewobject *dv) { Py_XDECREF(dv->dv_dict); - PyObject_Del(dv); + PyObject_GC_Del(dv); +} + +static int +dictview_traverse(dictviewobject *dv, visitproc visit, void *arg) +{ + Py_VISIT(dv->dv_dict); + return 0; } static Py_ssize_t @@ -2442,11 +2458,12 @@ dictview_new(PyObject *dict, PyTypeObject *type) type->tp_name, dict->ob_type->tp_name); return NULL; } - dv = PyObject_New(dictviewobject, type); + dv = PyObject_GC_New(dictviewobject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; + _PyObject_GC_TRACK(dv); return (PyObject *)dv; } @@ -2693,9 +2710,9 @@ PyTypeObject PyDictKeys_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -2777,9 +2794,9 @@ PyTypeObject PyDictItems_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -2842,9 +2859,9 @@ PyTypeObject PyDictValues_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ diff --git a/Objects/setobject.c b/Objects/setobject.c index eb223a3..d3243dd 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -802,7 +802,14 @@ static void setiter_dealloc(setiterobject *si) { Py_XDECREF(si->si_set); - PyObject_Del(si); + PyObject_GC_Del(si); +} + +static int +setiter_traverse(setiterobject *si, visitproc visit, void *arg) +{ + Py_VISIT(si->si_set); + return 0; } static PyObject * @@ -880,9 +887,9 @@ PyTypeObject PySetIter_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ - 0, /* tp_traverse */ + (traverseproc)setiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ @@ -895,7 +902,7 @@ PyTypeObject PySetIter_Type = { static PyObject * set_iter(PySetObject *so) { - setiterobject *si = PyObject_New(setiterobject, &PySetIter_Type); + setiterobject *si = PyObject_GC_New(setiterobject, &PySetIter_Type); if (si == NULL) return NULL; Py_INCREF(so); @@ -903,6 +910,7 @@ set_iter(PySetObject *so) si->si_used = so->used; si->si_pos = 0; si->len = so->used; + _PyObject_GC_TRACK(si); return (PyObject *)si; } -- cgit v0.12