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. ........
This commit is contained in:
parent
75edad0502
commit
7ddda7830c
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue