Skip to content

Commit 63a52d4

Browse files
committed
refactor: OrderedDict copy implement
1 parent 2775028 commit 63a52d4

File tree

2 files changed

+46
-64
lines changed

2 files changed

+46
-64
lines changed

Lib/test/test_ordered_dict.py

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -730,32 +730,6 @@ def test_merge_operator(self):
730730
with self.assertRaises(ValueError):
731731
a |= "BAD"
732732

733-
def test_getitem_re_entrant_clear_during_copy(self):
734-
class Evil(self.OrderedDict):
735-
def __getitem__(self, key):
736-
super().clear()
737-
return None
738-
739-
evil_dict = Evil([(i, i) for i in range(4)])
740-
result = evil_dict.copy()
741-
742-
self.assertEqual(len(result), 4)
743-
744-
def test_getitem_re_entrant_modify_during_copy(self):
745-
class Modifier(self.OrderedDict):
746-
def __getitem__(self, key):
747-
self['new_key'] = 'new_value'
748-
return super().__getitem__(key)
749-
750-
original = Modifier([(1, 'one'), (2, 'two')])
751-
result = original.copy()
752-
753-
self.assertIn(1, result)
754-
self.assertIn(2, result)
755-
self.assertEqual(result[1], 'one')
756-
self.assertEqual(result[2], 'two')
757-
self.assertEqual(result["new_key"], "new_value")
758-
759733
@support.cpython_only
760734
def test_ordered_dict_items_result_gc(self):
761735
# bpo-42536: OrderedDict.items's tuple-reuse speed trick breaks the GC's
@@ -900,6 +874,26 @@ def side_effect(self):
900874
self.assertDictEqual(dict1, dict.fromkeys((0, 4.2)))
901875
self.assertDictEqual(dict2, dict.fromkeys((0, Key(), 4.2)))
902876

877+
def test_getitem_re_entrant_clear_during_copy(self):
878+
class Evil(self.OrderedDict):
879+
def __getitem__(self, key):
880+
super().clear()
881+
return None
882+
883+
evil_dict = Evil([(i, i) for i in range(4)])
884+
with self.assertRaises(RuntimeError):
885+
result = evil_dict.copy()
886+
887+
def test_getitem_re_entrant_modify_during_copy(self):
888+
class Modifier(self.OrderedDict):
889+
def __getitem__(self, key):
890+
self['new_key'] = 'new_value'
891+
return super().__getitem__(key)
892+
893+
original = Modifier([(1, 'one'), (2, 'two')])
894+
with self.assertRaises(RuntimeError):
895+
result = original.copy()
896+
903897

904898
@unittest.skipUnless(c_coll, 'requires the C version of the collections module')
905899
class CPythonOrderedDictTests(OrderedDictTests,

Objects/odictobject.c

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,51 +1266,39 @@ OrderedDict_copy_impl(PyObject *od)
12661266
}
12671267
}
12681268
else {
1269-
Py_ssize_t i, size = PyODict_Size((PyODictObject *)od);
1270-
PyObject **keys;
1271-
if (size < 0) {
1272-
goto fail;
1273-
}
1274-
1275-
if (size == 0) {
1276-
return od_copy;
1277-
}
1278-
1279-
keys = PyMem_Malloc(size * sizeof(PyObject *));
1280-
if (keys == NULL) {
1281-
PyErr_NoMemory();
1282-
goto fail;
1283-
}
1269+
PyODictObject *self = _PyODictObject_CAST(od);
1270+
size_t state = self->od_state;
1271+
_ODictNode *cur = _odict_FIRST(od);
12841272

1285-
i = 0;
1286-
_odict_FOREACH(od, node) {
1287-
keys[i] = _odictnode_KEY(node);
1288-
Py_INCREF(keys[i]);
1289-
i++;
1290-
}
1291-
1292-
for (i = 0; i < size; i++) {
1293-
int res;
1294-
PyObject *value = PyObject_GetItem((PyObject *)od, keys[i]);
1273+
while (cur != NULL) {
1274+
if (self->od_state != state) {
1275+
PyErr_SetString(PyExc_RuntimeError,
1276+
"OrderedDict mutated during iteration");
1277+
goto fail;
1278+
}
1279+
PyObject *key = Py_NewRef(_odictnode_KEY(cur));
1280+
PyObject *value = PyObject_GetItem((PyObject *)od, key);
12951281
if (value == NULL) {
1296-
for (Py_ssize_t j = 0; j < size; j++) {
1297-
Py_DECREF(keys[j]);
1298-
}
1299-
PyMem_Free(keys);
1282+
Py_DECREF(key);
13001283
goto fail;
13011284
}
1302-
res = PyObject_SetItem((PyObject *)od_copy, keys[i], value);
1303-
Py_DECREF(value);
1304-
Py_DECREF(keys[i]);
1305-
if (res != 0) {
1306-
for (; i < size; i++) {
1307-
Py_DECREF(keys[i]);
1308-
}
1309-
PyMem_Free(keys);
1285+
if (self->od_state != state) {
1286+
Py_DECREF(key);
1287+
Py_DECREF(value);
1288+
PyErr_SetString(PyExc_RuntimeError,
1289+
"OrderedDict mutated during iteration");
13101290
goto fail;
13111291
}
1292+
if (PyObject_SetItem((PyObject *)od_copy, key, value) != 0) {
1293+
Py_DECREF(key);
1294+
Py_DECREF(value);
1295+
goto fail;
1296+
}
1297+
Py_DECREF(key);
1298+
Py_DECREF(value);
1299+
1300+
cur = _odictnode_NEXT(cur);
13121301
}
1313-
PyMem_Free(keys);
13141302
}
13151303
return od_copy;
13161304

0 commit comments

Comments
 (0)