From fd3be2998a0325224efb4e69b4dd0b75e3c32b3c Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 17 Feb 2026 22:24:13 +0900 Subject: [PATCH 01/18] gh-141510: Optimize {frozen}dict.fromkeys for frozendict --- Objects/dictobject.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 46b0148cf59ab5..7db60507ef3190 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3285,7 +3285,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) if (PyAnyDict_CheckExact(d)) { - if (PyAnyDict_CheckExact(iterable)) { + if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_BEGIN_CRITICAL_SECTION2(d, iterable); @@ -3293,6 +3293,14 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) Py_END_CRITICAL_SECTION2(); return d; } + else if (PyFrozenDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(d); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } else if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; @@ -3302,6 +3310,29 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return d; } } + else if (PyFrozenDict_CheckExact(d)) { + if (PyDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(iterable); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } + else if (PyFrozenDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + return d; + } + else if (PyAnySet_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(iterable); + d = (PyObject *)dict_set_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } + } it = PyObject_GetIter(iterable); if (it == NULL){ @@ -3309,7 +3340,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return NULL; } - if (PyAnyDict_CheckExact(d)) { + if (PyDict_CheckExact(d)) { Py_BEGIN_CRITICAL_SECTION(d); while ((key = PyIter_Next(it)) != NULL) { status = setitem_lock_held((PyDictObject *)d, key, value); From 36f2d77ef7d2f1b1a2851e0d8f025524d5603c1e Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 17 Feb 2026 22:27:19 +0900 Subject: [PATCH 02/18] Add NEWS.d --- .../2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst new file mode 100644 index 00000000000000..78a3e6c63da9a3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst @@ -0,0 +1,3 @@ +Optimize :meth:`dict.fromkeys` and :meth:`frozendict.fromkeys` to avoid +unnecessary thread-safety operations in frozendict cases. Patch by Donghee +Na. From f056fa63076f2fcf6d5ab126f2b45064c03d4747 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 17 Feb 2026 22:49:55 +0900 Subject: [PATCH 03/18] Update docs --- Doc/library/stdtypes.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 6f798f02e17899..8fabe04ed502aa 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5871,6 +5871,16 @@ Frozen dictionaries :class:`!frozendict` is not a :class:`!dict` subclass but inherits directly from ``object``. + .. classmethod:: fromkeys(iterable, value=None, /) + + Create a new frozen dictionary with keys from *iterable* and values set to + *value*. + + :meth:`fromkeys` is a class method that returns a new frozen dictionary. + *value* defaults to ``None``. All of the values refer to just a single + instance, so it generally doesn't make sense for *value* to be a mutable + object such as an empty list. + .. versionadded:: next From b51b1aaefe70bb9230d24433da6eddec7840a058 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 17 Feb 2026 23:12:54 +0900 Subject: [PATCH 04/18] fix --- Objects/dictobject.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 7db60507ef3190..1db821dfb1cc4f 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2671,10 +2671,8 @@ _PyDict_LoadBuiltinsFromGlobals(PyObject *globals) /* Consumes references to key and value */ static int -setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) +anydict_setitem_take2(PyDictObject *mp, PyObject *key, PyObject *value) { - ASSERT_DICT_LOCKED(mp); - assert(key); assert(value); assert(PyAnyDict_Check(mp)); @@ -2693,6 +2691,14 @@ setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) return insertdict(mp, key, hash, value); } +/* Consumes references to key and value */ +static int +setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value) +{ + ASSERT_DICT_LOCKED(mp); + return anydict_setitem_take2(mp, key, value); +} + int _PyDict_SetItem_Take2(PyDictObject *mp, PyObject *key, PyObject *value) { @@ -3352,7 +3358,19 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) } dict_iter_exit:; Py_END_CRITICAL_SECTION(); - } else { + } + else if (PyFrozenDict_CheckExact(d)) { + while ((key = PyIter_Next(it)) != NULL) { + status = anydict_setitem_take2((PyDictObject *)d, + Py_NewRef(key), Py_NewRef(value)); + Py_DECREF(key); + if (status < 0) { + assert(PyErr_Occurred()); + goto Fail; + } + } + } + else { while ((key = PyIter_Next(it)) != NULL) { status = PyObject_SetItem(d, key, value); Py_DECREF(key); From a49d630d19777c7d2f038f751c539f4e81e2e722 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 17 Feb 2026 23:28:57 +0900 Subject: [PATCH 05/18] Address code review --- Objects/dictobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 1db821dfb1cc4f..a53e5c18247a1c 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3290,7 +3290,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return NULL; - if (PyAnyDict_CheckExact(d)) { + if (PyDict_CheckExact(d)) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; From da588a1f08b874e9a625195509cd914acce98d4a Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 17 Feb 2026 23:51:15 +0900 Subject: [PATCH 06/18] Address code review --- Objects/dictobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index a53e5c18247a1c..aef488f4a68c98 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3362,8 +3362,7 @@ dict_iter_exit:; else if (PyFrozenDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { status = anydict_setitem_take2((PyDictObject *)d, - Py_NewRef(key), Py_NewRef(value)); - Py_DECREF(key); + key, Py_NewRef(value)); if (status < 0) { assert(PyErr_Occurred()); goto Fail; From 49e8a98b697040c7c5f6424c41a1454951d69039 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 00:08:47 +0900 Subject: [PATCH 07/18] Address code review --- Doc/library/stdtypes.rst | 10 ---------- .../2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst | 5 ++--- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 8fabe04ed502aa..6f798f02e17899 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5871,16 +5871,6 @@ Frozen dictionaries :class:`!frozendict` is not a :class:`!dict` subclass but inherits directly from ``object``. - .. classmethod:: fromkeys(iterable, value=None, /) - - Create a new frozen dictionary with keys from *iterable* and values set to - *value*. - - :meth:`fromkeys` is a class method that returns a new frozen dictionary. - *value* defaults to ``None``. All of the values refer to just a single - instance, so it generally doesn't make sense for *value* to be a mutable - object such as an empty list. - .. versionadded:: next diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst index 78a3e6c63da9a3..b031fb3c75dea7 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-02-17-22-27-11.gh-issue-141510.-4yYsf.rst @@ -1,3 +1,2 @@ -Optimize :meth:`dict.fromkeys` and :meth:`frozendict.fromkeys` to avoid -unnecessary thread-safety operations in frozendict cases. Patch by Donghee -Na. +Optimize :meth:`!frozendict.fromkeys` to avoid unnecessary thread-safety operations +in frozendict cases. Patch by Donghee Na. From 8d8fc98b17f3d8c91b494df9171d5073c4b94e95 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 00:28:38 +0900 Subject: [PATCH 08/18] Address code review --- Objects/dictobject.c | 45 +++----------------------------------------- 1 file changed, 3 insertions(+), 42 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index aef488f4a68c98..37b64089a4530c 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3290,33 +3290,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return NULL; - if (PyDict_CheckExact(d)) { - if (PyDict_CheckExact(iterable)) { - PyDictObject *mp = (PyDictObject *)d; - - Py_BEGIN_CRITICAL_SECTION2(d, iterable); - d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); - Py_END_CRITICAL_SECTION2(); - return d; - } - else if (PyFrozenDict_CheckExact(iterable)) { - PyDictObject *mp = (PyDictObject *)d; - - Py_BEGIN_CRITICAL_SECTION(d); - d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); - Py_END_CRITICAL_SECTION(); - return d; - } - else if (PyAnySet_CheckExact(iterable)) { - PyDictObject *mp = (PyDictObject *)d; - - Py_BEGIN_CRITICAL_SECTION2(d, iterable); - d = (PyObject *)dict_set_fromkeys(mp, iterable, value); - Py_END_CRITICAL_SECTION2(); - return d; - } - } - else if (PyFrozenDict_CheckExact(d)) { + if (PyAnyDict_CheckExact(d)) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; @@ -3333,7 +3307,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) else if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; - Py_BEGIN_CRITICAL_SECTION(iterable); + Py_BEGIN_CRITICAL_SECTION(d); d = (PyObject *)dict_set_fromkeys(mp, iterable, value); Py_END_CRITICAL_SECTION(); return d; @@ -3346,20 +3320,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return NULL; } - if (PyDict_CheckExact(d)) { - Py_BEGIN_CRITICAL_SECTION(d); - while ((key = PyIter_Next(it)) != NULL) { - status = setitem_lock_held((PyDictObject *)d, key, value); - Py_DECREF(key); - if (status < 0) { - assert(PyErr_Occurred()); - goto dict_iter_exit; - } - } -dict_iter_exit:; - Py_END_CRITICAL_SECTION(); - } - else if (PyFrozenDict_CheckExact(d)) { + if (PyAnyDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { status = anydict_setitem_take2((PyDictObject *)d, key, Py_NewRef(value)); From fa2505a082b657d4314356d5127173b8e7af492a Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 00:30:33 +0900 Subject: [PATCH 09/18] nit --- Objects/dictobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 37b64089a4530c..c743d1d2eb858d 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3322,8 +3322,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) if (PyAnyDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { - status = anydict_setitem_take2((PyDictObject *)d, - key, Py_NewRef(value)); + status = anydict_setitem_take2((PyDictObject *)d, key, Py_NewRef(value)); if (status < 0) { assert(PyErr_Occurred()); goto Fail; From fcef217f450bb5833f3bcd30e8c0b20de81559c8 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 00:49:14 +0900 Subject: [PATCH 10/18] fix --- Objects/dictobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index c743d1d2eb858d..4fa22410840ab0 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3307,7 +3307,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) else if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; - Py_BEGIN_CRITICAL_SECTION(d); + Py_BEGIN_CRITICAL_SECTION(iterable); d = (PyObject *)dict_set_fromkeys(mp, iterable, value); Py_END_CRITICAL_SECTION(); return d; From 0e7021703ce34fe9f2228696c373a746c8a00f16 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 01:12:28 +0900 Subject: [PATCH 11/18] fix --- Objects/dictobject.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 4fa22410840ab0..a91dcdbf62af68 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1899,8 +1899,6 @@ insertdict(PyDictObject *mp, PyObject *old_value = NULL; Py_ssize_t ix; - ASSERT_DICT_LOCKED(mp); - if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) { ix = insert_split_key(mp->ma_keys, key, hash); if (ix != DKIX_EMPTY) { @@ -1968,7 +1966,6 @@ insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { assert(mp->ma_keys == Py_EMPTY_KEYS); - ASSERT_DICT_LOCKED(mp); int unicode = PyUnicode_CheckExact(key); PyDictKeysObject *newkeys = new_keys_object(PyDict_LOG_MINSIZE, unicode); @@ -2752,6 +2749,7 @@ int _PyDict_SetItem_KnownHash_LockHeld(PyDictObject *mp, PyObject *key, PyObject *value, Py_hash_t hash) { + ASSERT_DICT_LOCKED(mp); if (mp->ma_keys == Py_EMPTY_KEYS) { return insert_to_emptydict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } From 305b2ef30415029b2fbbf8c3e059b09646db7b89 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 01:31:07 +0900 Subject: [PATCH 12/18] fix --- Objects/dictobject.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index a91dcdbf62af68..4ec6263aeeec42 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2069,8 +2069,6 @@ dictresize(PyDictObject *mp, PyDictKeysObject *oldkeys, *newkeys; PyDictValues *oldvalues; - ASSERT_DICT_LOCKED(mp); - if (log2_newsize >= SIZEOF_SIZE_T*8) { PyErr_NoMemory(); return -1; From 0a1b4004ef5ec20ddff3a89affdfc8a26b511572 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 01:59:04 +0900 Subject: [PATCH 13/18] Revert "fix" This reverts commit 305b2ef30415029b2fbbf8c3e059b09646db7b89. --- Objects/dictobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 4ec6263aeeec42..a91dcdbf62af68 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2069,6 +2069,8 @@ dictresize(PyDictObject *mp, PyDictKeysObject *oldkeys, *newkeys; PyDictValues *oldvalues; + ASSERT_DICT_LOCKED(mp); + if (log2_newsize >= SIZEOF_SIZE_T*8) { PyErr_NoMemory(); return -1; From 5e2757507bdcc0e1301e58b67f37686e9801ac57 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 01:59:16 +0900 Subject: [PATCH 14/18] Revert "fix" This reverts commit 0e7021703ce34fe9f2228696c373a746c8a00f16. --- Objects/dictobject.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index a91dcdbf62af68..4fa22410840ab0 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1899,6 +1899,8 @@ insertdict(PyDictObject *mp, PyObject *old_value = NULL; Py_ssize_t ix; + ASSERT_DICT_LOCKED(mp); + if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) { ix = insert_split_key(mp->ma_keys, key, hash); if (ix != DKIX_EMPTY) { @@ -1966,6 +1968,7 @@ insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { assert(mp->ma_keys == Py_EMPTY_KEYS); + ASSERT_DICT_LOCKED(mp); int unicode = PyUnicode_CheckExact(key); PyDictKeysObject *newkeys = new_keys_object(PyDict_LOG_MINSIZE, unicode); @@ -2749,7 +2752,6 @@ int _PyDict_SetItem_KnownHash_LockHeld(PyDictObject *mp, PyObject *key, PyObject *value, Py_hash_t hash) { - ASSERT_DICT_LOCKED(mp); if (mp->ma_keys == Py_EMPTY_KEYS) { return insert_to_emptydict(mp, Py_NewRef(key), hash, Py_NewRef(value)); } From 6c8d856c3dba25f49561f1f0ccedd9c9b7019234 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 01:59:28 +0900 Subject: [PATCH 15/18] Revert "fix" This reverts commit fcef217f450bb5833f3bcd30e8c0b20de81559c8. --- Objects/dictobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 4fa22410840ab0..c743d1d2eb858d 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3307,7 +3307,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) else if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; - Py_BEGIN_CRITICAL_SECTION(iterable); + Py_BEGIN_CRITICAL_SECTION(d); d = (PyObject *)dict_set_fromkeys(mp, iterable, value); Py_END_CRITICAL_SECTION(); return d; From 26c46c7fff515a8e85c718ae613a466c3c3db92b Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 01:59:49 +0900 Subject: [PATCH 16/18] Revert "nit" This reverts commit fa2505a082b657d4314356d5127173b8e7af492a. --- Objects/dictobject.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index c743d1d2eb858d..37b64089a4530c 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3322,7 +3322,8 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) if (PyAnyDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { - status = anydict_setitem_take2((PyDictObject *)d, key, Py_NewRef(value)); + status = anydict_setitem_take2((PyDictObject *)d, + key, Py_NewRef(value)); if (status < 0) { assert(PyErr_Occurred()); goto Fail; From d2fb5a068dd4da6cb745a2b2d7363a400c544785 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 02:00:04 +0900 Subject: [PATCH 17/18] Revert "Address code review" This reverts commit 8d8fc98b17f3d8c91b494df9171d5073c4b94e95. --- Objects/dictobject.c | 45 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 37b64089a4530c..aef488f4a68c98 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3290,7 +3290,33 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return NULL; - if (PyAnyDict_CheckExact(d)) { + if (PyDict_CheckExact(d)) { + if (PyDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION2(d, iterable); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION2(); + return d; + } + else if (PyFrozenDict_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION(d); + d = (PyObject *)dict_dict_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION(); + return d; + } + else if (PyAnySet_CheckExact(iterable)) { + PyDictObject *mp = (PyDictObject *)d; + + Py_BEGIN_CRITICAL_SECTION2(d, iterable); + d = (PyObject *)dict_set_fromkeys(mp, iterable, value); + Py_END_CRITICAL_SECTION2(); + return d; + } + } + else if (PyFrozenDict_CheckExact(d)) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; @@ -3307,7 +3333,7 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) else if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; - Py_BEGIN_CRITICAL_SECTION(d); + Py_BEGIN_CRITICAL_SECTION(iterable); d = (PyObject *)dict_set_fromkeys(mp, iterable, value); Py_END_CRITICAL_SECTION(); return d; @@ -3320,7 +3346,20 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) return NULL; } - if (PyAnyDict_CheckExact(d)) { + if (PyDict_CheckExact(d)) { + Py_BEGIN_CRITICAL_SECTION(d); + while ((key = PyIter_Next(it)) != NULL) { + status = setitem_lock_held((PyDictObject *)d, key, value); + Py_DECREF(key); + if (status < 0) { + assert(PyErr_Occurred()); + goto dict_iter_exit; + } + } +dict_iter_exit:; + Py_END_CRITICAL_SECTION(); + } + else if (PyFrozenDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { status = anydict_setitem_take2((PyDictObject *)d, key, Py_NewRef(value)); From e7c7dffe40470699889aec97cb109a76abb0a934 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Wed, 18 Feb 2026 06:17:20 +0900 Subject: [PATCH 18/18] Address code review --- Objects/dictobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index aef488f4a68c98..1c61db68b385a0 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -3361,6 +3361,7 @@ dict_iter_exit:; } else if (PyFrozenDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { + // anydict_setitem_take2 consumes a reference to key status = anydict_setitem_take2((PyDictObject *)d, key, Py_NewRef(value)); if (status < 0) {