Skip to content

Commit d1f2cb3

Browse files
committed
only fast-path fromkeys() when the constructor returns a empty dict (closes #16345)
1 parent c431128 commit d1f2cb3

File tree

3 files changed

+45
-33
lines changed

3 files changed

+45
-33
lines changed

Lib/test/test_dict.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,14 @@ def __setitem__(self, key, value):
254254
d = dict(zip(range(6), range(6)))
255255
self.assertEqual(dict.fromkeys(d, 0), dict(zip(range(6), [0]*6)))
256256

257+
class baddict3(dict):
258+
def __new__(cls):
259+
return d
260+
d = {i : i for i in range(10)}
261+
res = d.copy()
262+
res.update(a=None, b=None, c=None)
263+
self.assertEqual(baddict3.fromkeys({"a", "b", "c"}), res)
264+
257265
def test_copy(self):
258266
d = {1:1, 2:2, 3:3}
259267
self.assertEqual(d.copy(), {1:1, 2:2, 3:3})

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.2.4
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #16345: Fix an infinite loop when ``fromkeys`` on a dict subclass
14+
recieved a nonempty dict from the constructor.
15+
1316
- Issue #16197: Update winreg docstrings and documentation to match code.
1417
Patch by Zachary Ware.
1518

Objects/dictobject.c

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,49 +1335,50 @@ dict_fromkeys(PyObject *cls, PyObject *args)
13351335
if (d == NULL)
13361336
return NULL;
13371337

1338-
if (PyDict_CheckExact(d) && PyDict_CheckExact(seq)) {
1339-
PyDictObject *mp = (PyDictObject *)d;
1340-
PyObject *oldvalue;
1341-
Py_ssize_t pos = 0;
1342-
PyObject *key;
1343-
Py_hash_t hash;
1344-
1345-
if (dictresize(mp, Py_SIZE(seq))) {
1346-
Py_DECREF(d);
1347-
return NULL;
1348-
}
1349-
1350-
while (_PyDict_Next(seq, &pos, &key, &oldvalue, &hash)) {
1351-
Py_INCREF(key);
1352-
Py_INCREF(value);
1353-
if (insertdict(mp, key, hash, value)) {
1338+
if (PyDict_CheckExact(d) && PyDict_Size(d) == 0) {
1339+
if (PyDict_CheckExact(seq)) {
1340+
PyDictObject *mp = (PyDictObject *)d;
1341+
PyObject *oldvalue;
1342+
Py_ssize_t pos = 0;
1343+
PyObject *key;
1344+
Py_hash_t hash;
1345+
1346+
if (dictresize(mp, Py_SIZE(seq))) {
13541347
Py_DECREF(d);
13551348
return NULL;
13561349
}
1357-
}
1358-
return d;
1359-
}
13601350

1361-
if (PyDict_CheckExact(d) && PyAnySet_CheckExact(seq)) {
1362-
PyDictObject *mp = (PyDictObject *)d;
1363-
Py_ssize_t pos = 0;
1364-
PyObject *key;
1365-
Py_hash_t hash;
1366-
1367-
if (dictresize(mp, PySet_GET_SIZE(seq))) {
1368-
Py_DECREF(d);
1369-
return NULL;
1351+
while (_PyDict_Next(seq, &pos, &key, &oldvalue, &hash)) {
1352+
Py_INCREF(key);
1353+
Py_INCREF(value);
1354+
if (insertdict(mp, key, hash, value)) {
1355+
Py_DECREF(d);
1356+
return NULL;
1357+
}
1358+
}
1359+
return d;
13701360
}
1361+
if (PyAnySet_CheckExact(seq)) {
1362+
PyDictObject *mp = (PyDictObject *)d;
1363+
Py_ssize_t pos = 0;
1364+
PyObject *key;
1365+
Py_hash_t hash;
13711366

1372-
while (_PySet_NextEntry(seq, &pos, &key, &hash)) {
1373-
Py_INCREF(key);
1374-
Py_INCREF(value);
1375-
if (insertdict(mp, key, hash, value)) {
1367+
if (dictresize(mp, PySet_GET_SIZE(seq))) {
13761368
Py_DECREF(d);
13771369
return NULL;
13781370
}
1371+
1372+
while (_PySet_NextEntry(seq, &pos, &key, &hash)) {
1373+
Py_INCREF(key);
1374+
Py_INCREF(value);
1375+
if (insertdict(mp, key, hash, value)) {
1376+
Py_DECREF(d);
1377+
return NULL;
1378+
}
1379+
}
1380+
return d;
13791381
}
1380-
return d;
13811382
}
13821383

13831384
it = PyObject_GetIter(seq);

0 commit comments

Comments
 (0)