]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-117750: When clearing object's dict, clear inline values but leave dict attached...
authorMark Shannon <mark@hotpy.org>
Mon, 15 Apr 2024 13:45:05 +0000 (14:45 +0100)
committerGitHub <noreply@github.com>
Mon, 15 Apr 2024 13:45:05 +0000 (14:45 +0100)
Lib/test/test_class.py
Misc/NEWS.d/next/Core and Builtins/2024-04-12-11-19-18.gh-issue-117750.YttK6h.rst [new file with mode: 0644]
Objects/dictobject.c

index 4c1814142736e3a019d7206fd53dfbc18c46a8cd..a9cfd8df6918453aa222b51a7bb1a9e6d5628253 100644 (file)
@@ -862,6 +862,16 @@ class TestInlineValues(unittest.TestCase):
         self.assertFalse(has_inline_values(c))
         self.check_100(c)
 
+    def test_bug_117750(self):
+        "Aborted on 3.13a6"
+        class C:
+            def __init__(self):
+                self.__dict__.clear()
+
+        obj = C()
+        self.assertEqual(obj.__dict__, {})
+        obj.foo = None # Aborted here
+        self.assertEqual(obj.__dict__, {"foo":None})
 
 
 if __name__ == '__main__':
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-12-11-19-18.gh-issue-117750.YttK6h.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-12-11-19-18.gh-issue-117750.YttK6h.rst
new file mode 100644 (file)
index 0000000..d7cf5d6
--- /dev/null
@@ -0,0 +1,3 @@
+Fix issue where an object's dict would get out of sync with the object's
+internal values when being cleared. ``obj.__dict__.clear()`` now clears the
+internal values, but leaves the dict attached to the object.
index 9c38ef2314684f3e38f6e4d62e3a98ba92244267..003a03fd7417025da924403613b0386820068532 100644 (file)
@@ -2681,25 +2681,28 @@ clear_lock_held(PyObject *op)
             interp, PyDict_EVENT_CLEARED, mp, NULL, NULL);
     // We don't inc ref empty keys because they're immortal
     ensure_shared_on_resize(mp);
-
-    set_keys(mp, Py_EMPTY_KEYS);
-    set_values(mp, NULL);
-    mp->ma_used = 0;
     mp->ma_version_tag = new_version;
-    /* ...then clear the keys and values */
-    if (oldvalues != NULL) {
-        if (!oldvalues->embedded) {
-            n = oldkeys->dk_nentries;
-            for (i = 0; i < n; i++)
-                Py_CLEAR(oldvalues->values[i]);
-            free_values(oldvalues, IS_DICT_SHARED(mp));
-        }
-        dictkeys_decref(interp, oldkeys, false);
-    }
-    else {
+    mp->ma_used = 0;
+    if (oldvalues == NULL) {
+        set_keys(mp, Py_EMPTY_KEYS);
         assert(oldkeys->dk_refcnt == 1);
         dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
     }
+    else {
+        n = oldkeys->dk_nentries;
+        for (i = 0; i < n; i++) {
+            Py_CLEAR(oldvalues->values[i]);
+        }
+        if (oldvalues->embedded) {
+            oldvalues->size = 0;
+        }
+        else {
+            set_values(mp, NULL);
+            set_keys(mp, Py_EMPTY_KEYS);
+            free_values(oldvalues, IS_DICT_SHARED(mp));
+            dictkeys_decref(interp, oldkeys, false);
+        }
+    }
     ASSERT_CONSISTENT(mp);
 }