Skip to content

Commit 25284a2

Browse files
Issue #25935: Garbage collector now breaks reference loops with OrderedDict.
1 parent 8e1a4f1 commit 25284a2

File tree

3 files changed

+29
-13
lines changed

3 files changed

+29
-13
lines changed

Lib/test/test_ordered_dict.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import contextlib
22
import copy
3+
import gc
34
import pickle
45
from random import randrange, shuffle
56
import struct
67
import sys
78
import unittest
9+
import weakref
810
from collections.abc import MutableMapping
911
from test import mapping_tests, support
1012

@@ -585,6 +587,17 @@ def test_dict_update(self):
585587
dict.update(od, [('spam', 1)])
586588
self.assertNotIn('NULL', repr(od))
587589

590+
def test_reference_loop(self):
591+
# Issue 25935
592+
OrderedDict = self.OrderedDict
593+
class A:
594+
od = OrderedDict()
595+
A.od[A] = None
596+
r = weakref.ref(A)
597+
del A
598+
gc.collect()
599+
self.assertIsNone(r())
600+
588601

589602
class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase):
590603

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ Core and Builtins
4646
Library
4747
-------
4848

49+
- Issue #25935: Garbage collector now breaks reference loops with OrderedDict.
50+
4951
- Issue #16620: Fixed AttributeError in msilib.Directory.glob().
5052

5153
- Issue #26013: Added compatibility with broken protocol 2 pickles created

Objects/odictobject.c

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -772,19 +772,17 @@ _odict_clear_nodes(PyODictObject *od)
772772
{
773773
_ODictNode *node, *next;
774774

775-
if (!_odict_EMPTY(od)) {
776-
node = _odict_FIRST(od);
777-
while (node != NULL) {
778-
next = _odictnode_NEXT(node);
779-
_odictnode_DEALLOC(node);
780-
node = next;
781-
}
782-
_odict_FIRST(od) = NULL;
783-
_odict_LAST(od) = NULL;
784-
}
785-
786775
_odict_free_fast_nodes(od);
787776
od->od_fast_nodes = NULL;
777+
778+
node = _odict_FIRST(od);
779+
_odict_FIRST(od) = NULL;
780+
_odict_LAST(od) = NULL;
781+
while (node != NULL) {
782+
next = _odictnode_NEXT(node);
783+
_odictnode_DEALLOC(node);
784+
node = next;
785+
}
788786
}
789787

790788
/* There isn't any memory management of nodes past this point. */
@@ -1233,8 +1231,6 @@ odict_clear(register PyODictObject *od)
12331231
{
12341232
PyDict_Clear((PyObject *)od);
12351233
_odict_clear_nodes(od);
1236-
_odict_FIRST(od) = NULL;
1237-
_odict_LAST(od) = NULL;
12381234
if (_odict_resize(od) < 0)
12391235
return NULL;
12401236
Py_RETURN_NONE;
@@ -1556,8 +1552,13 @@ PyDoc_STRVAR(odict_doc,
15561552
static int
15571553
odict_traverse(PyODictObject *od, visitproc visit, void *arg)
15581554
{
1555+
_ODictNode *node;
1556+
15591557
Py_VISIT(od->od_inst_dict);
15601558
Py_VISIT(od->od_weakreflist);
1559+
_odict_FOREACH(od, node) {
1560+
Py_VISIT(_odictnode_KEY(node));
1561+
}
15611562
return PyDict_Type.tp_traverse((PyObject *)od, visit, arg);
15621563
}
15631564

0 commit comments

Comments
 (0)