Skip to content

Commit 16ccdbc

Browse files
authored
pythongh-141510: Fix frozendict.fromkeys() for dict subclasses (python#144962)
Copy also the dictionary if a dict subclass returns a frozendict.
1 parent 3e2f5c1 commit 16ccdbc

File tree

2 files changed

+20
-6
lines changed

2 files changed

+20
-6
lines changed

Lib/test/test_dict.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,16 @@ class FrozenDictSubclass2(frozendict):
18151815
self.assertEqual(fd, frozendict(a=None, b=None, c=None))
18161816
self.assertEqual(type(fd), FrozenDictSubclass2)
18171817

1818+
# Dict subclass which overrides the constructor
1819+
class DictSubclass(dict):
1820+
def __new__(self):
1821+
return created
1822+
1823+
fd = DictSubclass.fromkeys("abc")
1824+
self.assertEqual(fd, frozendict(x=1, a=None, b=None, c=None))
1825+
self.assertEqual(type(fd), DictSubclass)
1826+
self.assertEqual(created, frozendict(x=1))
1827+
18181828

18191829
if __name__ == "__main__":
18201830
unittest.main()

Objects/dictobject.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ As a consequence of this, split keys have a maximum size of 16.
138138
// Forward declarations
139139
static PyObject* frozendict_new(PyTypeObject *type, PyObject *args,
140140
PyObject *kwds);
141+
static PyObject* dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
141142
static int dict_merge(PyObject *a, PyObject *b, int override);
142143

143144

@@ -3305,15 +3306,18 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
33053306
return NULL;
33063307
}
33073308

3308-
// If cls is a frozendict subclass with overridden constructor,
3309+
// If cls is a dict or frozendict subclass with overridden constructor,
33093310
// copy the frozendict.
33103311
PyTypeObject *cls_type = _PyType_CAST(cls);
3311-
if (PyFrozenDict_Check(d)
3312-
&& PyObject_IsSubclass(cls, (PyObject*)&PyFrozenDict_Type)
3313-
&& cls_type->tp_new != frozendict_new)
3314-
{
3312+
if (PyFrozenDict_Check(d) && cls_type->tp_new != frozendict_new) {
33153313
// Subclass-friendly copy
3316-
PyObject *copy = frozendict_new(cls_type, NULL, NULL);
3314+
PyObject *copy;
3315+
if (PyObject_IsSubclass(cls, (PyObject*)&PyFrozenDict_Type)) {
3316+
copy = frozendict_new(cls_type, NULL, NULL);
3317+
}
3318+
else {
3319+
copy = dict_new(cls_type, NULL, NULL);
3320+
}
33173321
if (copy == NULL) {
33183322
Py_DECREF(d);
33193323
return NULL;

0 commit comments

Comments
 (0)