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]
