gh-141510: Fix frozendict.fromkeys() for dict subclasses (#144962)

Copy also the dictionary if a dict subclass returns a frozendict.
This commit is contained in:
Victor Stinner 2026-02-18 18:03:04 +01:00 committed by GitHub
parent 3e2f5c133f
commit 16ccdbc50a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 20 additions and 6 deletions

View file

@ -1815,6 +1815,16 @@ class FrozenDictSubclass2(frozendict):
self.assertEqual(fd, frozendict(a=None, b=None, c=None))
self.assertEqual(type(fd), FrozenDictSubclass2)
# Dict subclass which overrides the constructor
class DictSubclass(dict):
def __new__(self):
return created
fd = DictSubclass.fromkeys("abc")
self.assertEqual(fd, frozendict(x=1, a=None, b=None, c=None))
self.assertEqual(type(fd), DictSubclass)
self.assertEqual(created, frozendict(x=1))
if __name__ == "__main__":
unittest.main()

View file

@ -138,6 +138,7 @@ As a consequence of this, split keys have a maximum size of 16.
// Forward declarations
static PyObject* frozendict_new(PyTypeObject *type, PyObject *args,
PyObject *kwds);
static PyObject* dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static int dict_merge(PyObject *a, PyObject *b, int override);
@ -3305,15 +3306,18 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
return NULL;
}
// If cls is a frozendict subclass with overridden constructor,
// If cls is a dict or frozendict subclass with overridden constructor,
// copy the frozendict.
PyTypeObject *cls_type = _PyType_CAST(cls);
if (PyFrozenDict_Check(d)
&& PyObject_IsSubclass(cls, (PyObject*)&PyFrozenDict_Type)
&& cls_type->tp_new != frozendict_new)
{
if (PyFrozenDict_Check(d) && cls_type->tp_new != frozendict_new) {
// Subclass-friendly copy
PyObject *copy = frozendict_new(cls_type, NULL, NULL);
PyObject *copy;
if (PyObject_IsSubclass(cls, (PyObject*)&PyFrozenDict_Type)) {
copy = frozendict_new(cls_type, NULL, NULL);
}
else {
copy = dict_new(cls_type, NULL, NULL);
}
if (copy == NULL) {
Py_DECREF(d);
return NULL;