https://github.com/python/cpython/commit/8c212aa0de538baeaa71f9de73d79f360f1116d3
commit: 8c212aa0de538baeaa71f9de73d79f360f1116d3
branch: main
author: Nishant Bhandari <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-11-09T22:16:40Z
summary:

gh-158525: Avoid reacquiring dict lock when copying in free-threaded build 
(#158535)

Co-authored-by: Kumar Aditya <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-10-01-00-00-00.gh-issue-158525.Xw3K9a.rst
M Lib/test/test_dict.py
M Objects/dictobject.c

diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index ea697a5a920f77..e8b2aeb1d53125 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -1958,6 +1958,20 @@ def test_copy(self):
         self.assertEqual(d2, frozendict(x=1, y=2))
         self.assertEqual(type(d2), frozendict)
 
+    def test_copy_iter_override(self):
+        class CustomReversedDict(frozendict):
+            def keys(self):
+                return reversed(list(frozendict.keys(self)))
+
+            __iter__ = keys
+
+        pairs = [('a', 1), ('b', 2), ('c', 3)]
+        d = CustomReversedDict(pairs)
+        d2 = d.copy()
+        self.assertIsNot(d2, d)
+        self.assertEqual(type(d2), frozendict)
+        self.assertEqual(list(d2.items()), pairs[::-1])
+
     def test_merge(self):
         # test "a | b" operator
         self.assertEqual(frozendict(x=1) | frozendict(y=2),
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-01-00-00-00.gh-issue-158525.Xw3K9a.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-01-00-00-00.gh-issue-158525.Xw3K9a.rst
new file mode 100644
index 00000000000000..18ab4418020d1f
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-01-00-00-00.gh-issue-158525.Xw3K9a.rst
@@ -0,0 +1 @@
+Fix slow ``dict.copy()`` for dicts with many deleted entries in the 
free-threaded build.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 88a8d61828503f..b8ca74aedc2adf 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -4300,7 +4300,9 @@ static int
 dict_dict_merge(PyDictObject *mp, PyDictObject *other, int override, PyObject 
**dupkey)
 {
     assert(can_modify_dict(mp));
-    ASSERT_DICT_LOCKED(other);
+    if (!PyFrozenDict_Check(other)) {
+        ASSERT_DICT_LOCKED(other);
+    }
 
     if (other == mp || other->ma_used == 0)
         /* a.update(a) or a.update({}); nothing to do */
@@ -4666,7 +4668,14 @@ copy_lock_held_untracked(PyObject *o, int as_frozendict)
     }
     if (copy == NULL)
         return NULL;
-    if (dict_merge(copy, o, 1, NULL) < 0) {
+    int res;
+    if (Py_TYPE(mp)->tp_iter == dict_iter) {
+        res = dict_dict_merge((PyDictObject *)copy, mp, 1, NULL);
+    }
+    else {
+        res = dict_merge(copy, o, 1, NULL);
+    }
+    if (res < 0) {
         Py_DECREF(copy);
         return NULL;
     }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to