diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 3b9ff672a1aa03..9ab0f994045ca7 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -1894,6 +1894,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..43e5bcbf1817b0 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-01-00-00-00.gh-issue-158525.Xw3K9a.rst @@ -0,0 +1,2 @@ +Avoid reacquiring the source dictionary lock when copying a sparse dictionary +in the free-threaded build. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 15377ac083c2b2..e5bf7c0da4b05d 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4209,7 +4209,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 */ @@ -4575,7 +4577,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; }