(original) (raw)
changeset: 99985:627575e9a7aa branch: 3.5 parent: 99981:652a50208e89 user: Serhiy Storchaka storchaka@gmail.com date: Tue Jan 19 14:46:25 2016 +0200 files: Lib/test/test_ordered_dict.py Misc/NEWS Objects/odictobject.c description: Issue #25935: Garbage collector now breaks reference loops with OrderedDict. diff -r 652a50208e89 -r 627575e9a7aa Lib/test/test_ordered_dict.py --- a/Lib/test/test_ordered_dict.py Tue Jan 19 13:55:36 2016 +0200 +++ b/Lib/test/test_ordered_dict.py Tue Jan 19 14:46:25 2016 +0200 @@ -1,10 +1,12 @@ import contextlib import copy +import gc import pickle from random import randrange, shuffle import struct import sys import unittest +import weakref from collections.abc import MutableMapping from test import mapping_tests, support @@ -585,6 +587,17 @@ dict.update(od, [('spam', 1)]) self.assertNotIn('NULL', repr(od)) + def test_reference_loop(self): + # Issue 25935 + OrderedDict = self.OrderedDict + class A: + od = OrderedDict() + A.od[A] = None + r = weakref.ref(A) + del A + gc.collect() + self.assertIsNone(r()) + class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase): diff -r 652a50208e89 -r 627575e9a7aa Misc/NEWS --- a/Misc/NEWS Tue Jan 19 13:55:36 2016 +0200 +++ b/Misc/NEWS Tue Jan 19 14:46:25 2016 +0200 @@ -46,6 +46,8 @@ Library ------- +- Issue #25935: Garbage collector now breaks reference loops with OrderedDict. + - Issue #16620: Fixed AttributeError in msilib.Directory.glob(). - Issue #26013: Added compatibility with broken protocol 2 pickles created diff -r 652a50208e89 -r 627575e9a7aa Objects/odictobject.c --- a/Objects/odictobject.c Tue Jan 19 13:55:36 2016 +0200 +++ b/Objects/odictobject.c Tue Jan 19 14:46:25 2016 +0200 @@ -772,19 +772,17 @@ { _ODictNode *node, *next; - if (!_odict_EMPTY(od)) { - node = _odict_FIRST(od); - while (node != NULL) { - next = _odictnode_NEXT(node); - _odictnode_DEALLOC(node); - node = next; - } - _odict_FIRST(od) = NULL; - _odict_LAST(od) = NULL; - } - _odict_free_fast_nodes(od); od->od_fast_nodes = NULL; + + node = _odict_FIRST(od); + _odict_FIRST(od) = NULL; + _odict_LAST(od) = NULL; + while (node != NULL) { + next = _odictnode_NEXT(node); + _odictnode_DEALLOC(node); + node = next; + } } /* There isn't any memory management of nodes past this point. */ @@ -1233,8 +1231,6 @@ { PyDict_Clear((PyObject *)od); _odict_clear_nodes(od); - _odict_FIRST(od) = NULL; - _odict_LAST(od) = NULL; if (_odict_resize(od) < 0) return NULL; Py_RETURN_NONE; @@ -1556,8 +1552,13 @@ static int odict_traverse(PyODictObject *od, visitproc visit, void *arg) { + _ODictNode *node; + Py_VISIT(od->od_inst_dict); Py_VISIT(od->od_weakreflist); + _odict_FOREACH(od, node) { + Py_VISIT(_odictnode_KEY(node)); + } return PyDict_Type.tp_traverse((PyObject *)od, visit, arg); } /storchaka@gmail.com