diff options
-rw-r--r-- | Lib/test/test_collections.py | 54 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Objects/odictobject.c | 15 |
3 files changed, 70 insertions, 2 deletions
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 3f5304b..b9fdf79 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -2145,6 +2145,60 @@ class OrderedDictTests: key = c0 + c1 od[key] = key + # Direct use of dict methods + + def test_dict_setitem(self): + OrderedDict = self.OrderedDict + od = OrderedDict() + dict.__setitem__(od, 'spam', 1) + self.assertNotIn('NULL', repr(od)) + + def test_dict_delitem(self): + OrderedDict = self.OrderedDict + od = OrderedDict() + od['spam'] = 1 + od['ham'] = 2 + dict.__delitem__(od, 'spam') + with self.assertRaises(KeyError): + repr(od) + + def test_dict_clear(self): + OrderedDict = self.OrderedDict + od = OrderedDict() + od['spam'] = 1 + od['ham'] = 2 + dict.clear(od) + self.assertNotIn('NULL', repr(od)) + + def test_dict_pop(self): + OrderedDict = self.OrderedDict + od = OrderedDict() + od['spam'] = 1 + od['ham'] = 2 + dict.pop(od, 'spam') + with self.assertRaises(KeyError): + repr(od) + + def test_dict_popitem(self): + OrderedDict = self.OrderedDict + od = OrderedDict() + od['spam'] = 1 + od['ham'] = 2 + dict.popitem(od) + with self.assertRaises(KeyError): + repr(od) + + def test_dict_setdefault(self): + OrderedDict = self.OrderedDict + od = OrderedDict() + dict.setdefault(od, 'spam', 1) + self.assertNotIn('NULL', repr(od)) + + def test_dict_update(self): + od = OrderedDict() + dict.update(od, [('spam', 1)]) + self.assertNotIn('NULL', repr(od)) + class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase): @@ -11,6 +11,9 @@ Release date: TBA Core and Builtins ----------------- +- Issue #25449: Fixed a crash and leaking NULL in repr() of OrderedDict that + was mutated by direct calls of dict methods. + - Issue #25449: Iterating OrderedDict with keys with unstable hash now raises KeyError in C implementations as well as in Python implementation. diff --git a/Objects/odictobject.c b/Objects/odictobject.c index d6189a3..28537a8 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1462,7 +1462,6 @@ odict_repr(PyODictObject *self) { int i; _Py_IDENTIFIER(items); - Py_ssize_t count = -1; PyObject *pieces = NULL, *result = NULL; const char *classname; @@ -1481,6 +1480,7 @@ odict_repr(PyODictObject *self) } if (PyODict_CheckExact(self)) { + Py_ssize_t count = 0; _ODictNode *node; pieces = PyList_New(PyODict_SIZE(self)); if (pieces == NULL) @@ -1499,8 +1499,19 @@ odict_repr(PyODictObject *self) if (pair == NULL) goto Done; - PyList_SET_ITEM(pieces, ++count, pair); /* steals reference */ + if (count < PyList_GET_SIZE(pieces)) + PyList_SET_ITEM(pieces, count, pair); /* steals reference */ + else { + if (PyList_Append(pieces, pair) < 0) { + Py_DECREF(pair); + goto Done; + } + Py_DECREF(pair); + } + count++; } + if (count < PyList_GET_SIZE(pieces)) + PyList_GET_SIZE(pieces) = count; } else { PyObject *items = _PyObject_CallMethodIdObjArgs((PyObject *)self, |