summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_class.py10
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-04-12-11-19-18.gh-issue-117750.YttK6h.rst3
-rw-r--r--Objects/dictobject.c33
3 files changed, 31 insertions, 15 deletions
diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py
index 4c18141..a9cfd8d 100644
--- a/Lib/test/test_class.py
+++ b/Lib/test/test_class.py
@@ -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
index 0000000..d7cf5d6
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-12-11-19-18.gh-issue-117750.YttK6h.rst
@@ -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.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 9c38ef2..003a03f 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -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);
}