]> granicus.if.org Git - python/commitdiff
Merged revisions 68128 via svnmerge from
authorAntoine Pitrou <solipsis@pitrou.net>
Thu, 1 Jan 2009 15:35:33 +0000 (15:35 +0000)
committerAntoine Pitrou <solipsis@pitrou.net>
Thu, 1 Jan 2009 15:35:33 +0000 (15:35 +0000)
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
Lib/test/test_dict.py
Lib/test/test_set.py
Misc/NEWS
Modules/_collectionsmodule.c
Objects/dictobject.c
Objects/setobject.c

index 7af0803e5d941b0c96b4227644806a62e4fbc05c..27c89c44897975895c0f7acb8da339091a68e70e 100644 (file)
@@ -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)
index 403d5ebf86cd52f9ecb37f8bf4bae7ae4e21e15d..2e74b68c3acf486db2d34f2baeae323b11b7cb66 100644 (file)
@@ -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
index da3ed0f53a66d36310a064181b7881f6a588c77d..5a8819f52a88f951ff7e6c79f7e61a46a25a5845 100644 (file)
@@ -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)
index d7adf6013960eda8b44a5b22011dd6f9d6335d7b..62b7a858d6e0bb76ec2b38460d351bffe2a33173 100644 (file)
--- 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.
 
index d7a60429a3e8581bd06f389c54390be20080953a..910c25f2d3bd0f71eaeb007e2dcd6a5f2916827d 100644 (file)
@@ -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 */
index 8a65b0ce51249983decad61d486b065468f8686d..186846649f013a57504e876085f17da40e53f667 100644 (file)
@@ -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 */
index eb223a3bb6275c659ffc8e6acf8ef85de5a37d8c..d3243dd77a3c77cfa21ae04486e9110b55539fb6 100644 (file)
@@ -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;
 }