Commit 7ddda783 authored by Antoine Pitrou's avatar Antoine Pitrou

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.
........
parent 75edad05
from collections import deque from collections import deque
import unittest import unittest
from test import support, seq_tests from test import support, seq_tests
from weakref import proxy import gc
import weakref
import copy import copy
import pickle import pickle
from io import StringIO from io import StringIO
...@@ -420,6 +421,22 @@ class TestBasic(unittest.TestCase): ...@@ -420,6 +421,22 @@ class TestBasic(unittest.TestCase):
d.append(1) d.append(1)
gc.collect() 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): class TestVariousIteratorArgs(unittest.TestCase):
def test_constructor(self): def test_constructor(self):
...@@ -530,7 +547,7 @@ class TestSubclass(unittest.TestCase): ...@@ -530,7 +547,7 @@ class TestSubclass(unittest.TestCase):
def test_weakref(self): def test_weakref(self):
d = deque('gallahad') d = deque('gallahad')
p = proxy(d) p = weakref.proxy(d)
self.assertEqual(str(p), str(d)) self.assertEqual(str(p), str(d))
d = None d = None
self.assertRaises(ReferenceError, str, p) self.assertRaises(ReferenceError, str, p)
......
...@@ -2,6 +2,7 @@ import unittest ...@@ -2,6 +2,7 @@ import unittest
from test import support from test import support
import sys, collections, random, string import sys, collections, random, string
import gc, weakref
class DictTest(unittest.TestCase): class DictTest(unittest.TestCase):
...@@ -648,6 +649,21 @@ class DictTest(unittest.TestCase): ...@@ -648,6 +649,21 @@ class DictTest(unittest.TestCase):
pass pass
d = {} 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 from test import mapping_tests
......
import unittest import unittest
from test import support from test import support
from weakref import proxy import gc
import weakref
import operator import operator
import copy import copy
import pickle import pickle
...@@ -323,6 +324,18 @@ class TestJointOps(unittest.TestCase): ...@@ -323,6 +324,18 @@ class TestJointOps(unittest.TestCase):
self.assertEqual(sum(elem.hash_count for elem in d), n) self.assertEqual(sum(elem.hash_count for elem in d), n)
self.assertEqual(d3, dict.fromkeys(d, 123)) 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): class TestSet(TestJointOps):
thetype = set thetype = set
basetype = set basetype = set
...@@ -546,7 +559,7 @@ class TestSet(TestJointOps): ...@@ -546,7 +559,7 @@ class TestSet(TestJointOps):
def test_weakref(self): def test_weakref(self):
s = self.thetype('gallahad') s = self.thetype('gallahad')
p = proxy(s) p = weakref.proxy(s)
self.assertEqual(str(p), str(s)) self.assertEqual(str(p), str(s))
s = None s = None
self.assertRaises(ReferenceError, str, p) self.assertRaises(ReferenceError, str, p)
......
...@@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 0 ...@@ -12,6 +12,9 @@ What's New in Python 3.1 alpha 0
Core and Builtins 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 - Issue #4701: PyObject_Hash now implicitly calls PyType_Ready on types
where the tp_hash and tp_dict slots are both NULL. where the tp_hash and tp_dict slots are both NULL.
......
...@@ -900,7 +900,7 @@ deque_iter(dequeobject *deque) ...@@ -900,7 +900,7 @@ deque_iter(dequeobject *deque)
{ {
dequeiterobject *it; dequeiterobject *it;
it = PyObject_New(dequeiterobject, &dequeiter_type); it = PyObject_GC_New(dequeiterobject, &dequeiter_type);
if (it == NULL) if (it == NULL)
return NULL; return NULL;
it->b = deque->leftblock; it->b = deque->leftblock;
...@@ -909,14 +909,22 @@ deque_iter(dequeobject *deque) ...@@ -909,14 +909,22 @@ deque_iter(dequeobject *deque)
it->deque = deque; it->deque = deque;
it->state = deque->state; it->state = deque->state;
it->counter = deque->len; it->counter = deque->len;
_PyObject_GC_TRACK(it);
return (PyObject *)it; return (PyObject *)it;
} }
static int
dequeiter_traverse(dequeiterobject *dio, visitproc visit, void *arg)
{
Py_VISIT(dio->deque);
return 0;
}
static void static void
dequeiter_dealloc(dequeiterobject *dio) dequeiter_dealloc(dequeiterobject *dio)
{ {
Py_XDECREF(dio->deque); Py_XDECREF(dio->deque);
Py_TYPE(dio)->tp_free(dio); PyObject_GC_Del(dio);
} }
static PyObject * static PyObject *
...@@ -981,9 +989,9 @@ static PyTypeObject dequeiter_type = { ...@@ -981,9 +989,9 @@ static PyTypeObject dequeiter_type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)dequeiter_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
0, /* tp_richcompare */ 0, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
...@@ -1002,7 +1010,7 @@ deque_reviter(dequeobject *deque) ...@@ -1002,7 +1010,7 @@ deque_reviter(dequeobject *deque)
{ {
dequeiterobject *it; dequeiterobject *it;
it = PyObject_New(dequeiterobject, &dequereviter_type); it = PyObject_GC_New(dequeiterobject, &dequereviter_type);
if (it == NULL) if (it == NULL)
return NULL; return NULL;
it->b = deque->rightblock; it->b = deque->rightblock;
...@@ -1011,6 +1019,7 @@ deque_reviter(dequeobject *deque) ...@@ -1011,6 +1019,7 @@ deque_reviter(dequeobject *deque)
it->deque = deque; it->deque = deque;
it->state = deque->state; it->state = deque->state;
it->counter = deque->len; it->counter = deque->len;
_PyObject_GC_TRACK(it);
return (PyObject *)it; return (PyObject *)it;
} }
...@@ -1063,9 +1072,9 @@ static PyTypeObject dequereviter_type = { ...@@ -1063,9 +1072,9 @@ static PyTypeObject dequereviter_type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)dequeiter_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
0, /* tp_richcompare */ 0, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
......
...@@ -2122,7 +2122,7 @@ static PyObject * ...@@ -2122,7 +2122,7 @@ static PyObject *
dictiter_new(PyDictObject *dict, PyTypeObject *itertype) dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
{ {
dictiterobject *di; dictiterobject *di;
di = PyObject_New(dictiterobject, itertype); di = PyObject_GC_New(dictiterobject, itertype);
if (di == NULL) if (di == NULL)
return NULL; return NULL;
Py_INCREF(dict); Py_INCREF(dict);
...@@ -2139,6 +2139,7 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype) ...@@ -2139,6 +2139,7 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
} }
else else
di->di_result = NULL; di->di_result = NULL;
_PyObject_GC_TRACK(di);
return (PyObject *)di; return (PyObject *)di;
} }
...@@ -2147,7 +2148,15 @@ dictiter_dealloc(dictiterobject *di) ...@@ -2147,7 +2148,15 @@ dictiter_dealloc(dictiterobject *di)
{ {
Py_XDECREF(di->di_dict); Py_XDECREF(di->di_dict);
Py_XDECREF(di->di_result); 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 * static PyObject *
...@@ -2228,9 +2237,9 @@ PyTypeObject PyDictIterKey_Type = { ...@@ -2228,9 +2237,9 @@ PyTypeObject PyDictIterKey_Type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)dictiter_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
0, /* tp_richcompare */ 0, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
...@@ -2300,9 +2309,9 @@ PyTypeObject PyDictIterValue_Type = { ...@@ -2300,9 +2309,9 @@ PyTypeObject PyDictIterValue_Type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)dictiter_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
0, /* tp_richcompare */ 0, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
...@@ -2386,9 +2395,9 @@ PyTypeObject PyDictIterItem_Type = { ...@@ -2386,9 +2395,9 @@ PyTypeObject PyDictIterItem_Type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)dictiter_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
0, /* tp_richcompare */ 0, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
...@@ -2415,7 +2424,14 @@ static void ...@@ -2415,7 +2424,14 @@ static void
dictview_dealloc(dictviewobject *dv) dictview_dealloc(dictviewobject *dv)
{ {
Py_XDECREF(dv->dv_dict); 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 static Py_ssize_t
...@@ -2442,11 +2458,12 @@ dictview_new(PyObject *dict, PyTypeObject *type) ...@@ -2442,11 +2458,12 @@ dictview_new(PyObject *dict, PyTypeObject *type)
type->tp_name, dict->ob_type->tp_name); type->tp_name, dict->ob_type->tp_name);
return NULL; return NULL;
} }
dv = PyObject_New(dictviewobject, type); dv = PyObject_GC_New(dictviewobject, type);
if (dv == NULL) if (dv == NULL)
return NULL; return NULL;
Py_INCREF(dict); Py_INCREF(dict);
dv->dv_dict = (PyDictObject *)dict; dv->dv_dict = (PyDictObject *)dict;
_PyObject_GC_TRACK(dv);
return (PyObject *)dv; return (PyObject *)dv;
} }
...@@ -2693,9 +2710,9 @@ PyTypeObject PyDictKeys_Type = { ...@@ -2693,9 +2710,9 @@ PyTypeObject PyDictKeys_Type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
dictview_richcompare, /* tp_richcompare */ dictview_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
...@@ -2777,9 +2794,9 @@ PyTypeObject PyDictItems_Type = { ...@@ -2777,9 +2794,9 @@ PyTypeObject PyDictItems_Type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
dictview_richcompare, /* tp_richcompare */ dictview_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
...@@ -2842,9 +2859,9 @@ PyTypeObject PyDictValues_Type = { ...@@ -2842,9 +2859,9 @@ PyTypeObject PyDictValues_Type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
0, /* tp_richcompare */ 0, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
......
...@@ -802,7 +802,14 @@ static void ...@@ -802,7 +802,14 @@ static void
setiter_dealloc(setiterobject *si) setiter_dealloc(setiterobject *si)
{ {
Py_XDECREF(si->si_set); 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 * static PyObject *
...@@ -880,9 +887,9 @@ PyTypeObject PySetIter_Type = { ...@@ -880,9 +887,9 @@ PyTypeObject PySetIter_Type = {
PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */ 0, /* tp_doc */
0, /* tp_traverse */ (traverseproc)setiter_traverse, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */
0, /* tp_richcompare */ 0, /* tp_richcompare */
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
...@@ -895,7 +902,7 @@ PyTypeObject PySetIter_Type = { ...@@ -895,7 +902,7 @@ PyTypeObject PySetIter_Type = {
static PyObject * static PyObject *
set_iter(PySetObject *so) set_iter(PySetObject *so)
{ {
setiterobject *si = PyObject_New(setiterobject, &PySetIter_Type); setiterobject *si = PyObject_GC_New(setiterobject, &PySetIter_Type);
if (si == NULL) if (si == NULL)
return NULL; return NULL;
Py_INCREF(so); Py_INCREF(so);
...@@ -903,6 +910,7 @@ set_iter(PySetObject *so) ...@@ -903,6 +910,7 @@ set_iter(PySetObject *so)
si->si_used = so->used; si->si_used = so->used;
si->si_pos = 0; si->si_pos = 0;
si->len = so->used; si->len = so->used;
_PyObject_GC_TRACK(si);
return (PyObject *)si; return (PyObject *)si;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment