]> granicus.if.org Git - python/commitdiff
Issue #22653: Fix an assertion failure in debug mode when doing a reentrant dict...
authorAntoine Pitrou <solipsis@pitrou.net>
Fri, 17 Oct 2014 22:35:00 +0000 (00:35 +0200)
committerAntoine Pitrou <solipsis@pitrou.net>
Fri, 17 Oct 2014 22:35:00 +0000 (00:35 +0200)
Lib/test/test_dict.py
Misc/NEWS
Objects/dictobject.c

index a38895945ed5be8499e028a2a0827f90dffbf0ca..98d8a3bd00ea8300349328859c3a574ad18563c0 100644 (file)
@@ -906,6 +906,35 @@ class DictTest(unittest.TestCase):
         f.a = 'a'
         self.assertEqual(f.__dict__, {1:1, 'a':'a'})
 
+    def check_reentrant_insertion(self, mutate):
+        # This object will trigger mutation of the dict when replaced
+        # by another value.  Note this relies on refcounting: the test
+        # won't achieve its purpose on fully-GCed Python implementations.
+        class Mutating:
+            def __del__(self):
+                mutate(d)
+
+        d = {k: Mutating() for k in 'abcdefghijklmnopqr'}
+        for k in list(d):
+            d[k] = k
+
+    def test_reentrant_insertion(self):
+        # Reentrant insertion shouldn't crash (see issue #22653)
+        def mutate(d):
+            d['b'] = 5
+        self.check_reentrant_insertion(mutate)
+
+        def mutate(d):
+            d.update(self.__dict__)
+            d.clear()
+        self.check_reentrant_insertion(mutate)
+
+        def mutate(d):
+            while d:
+                d.popitem()
+        self.check_reentrant_insertion(mutate)
+
+
 from test import mapping_tests
 
 class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
index 80721b0e3cc311c7da8436ce7b6b4539910a8920..2f02651ccc0f32587d1b6ba93eb4c12b74dedbda 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -11,6 +11,9 @@ Release date: TBA
 Core and Builtins
 -----------------
 
+- Issue #22653: Fix an assertion failure in debug mode when doing a reentrant
+  dict insertion in debug mode.
+
 - Issue #22643: Fix integer overflow in Unicode case operations (upper, lower,
   title, swapcase, casefold).
 
index 1ccea6ef2aaedc0e98c308ebea1dc24af803f940..bab6242631f7f1a119709bf08686dc055156ae70 100644 (file)
@@ -814,13 +814,14 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
     if (ep == NULL) {
         return -1;
     }
+    assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
     Py_INCREF(value);
     MAINTAIN_TRACKING(mp, key, value);
     old_value = *value_addr;
     if (old_value != NULL) {
         assert(ep->me_key != NULL && ep->me_key != dummy);
         *value_addr = value;
-        Py_DECREF(old_value); /* which **CAN** re-enter */
+        Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
     }
     else {
         if (ep->me_key == NULL) {
@@ -851,9 +852,8 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
         }
         mp->ma_used++;
         *value_addr = value;
+        assert(ep->me_key != NULL && ep->me_key != dummy);
     }
-    assert(ep->me_key != NULL && ep->me_key != dummy);
-    assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict);
     return 0;
 }