Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 66a0d1d

Browse files
committed
dict_update(): Generalize this method so {}.update() accepts any
"mapping" object, specifically one that supports PyMapping_Keys() and PyObject_GetItem(). This allows you to say e.g. {}.update(UserDict()) We keep the special case for concrete dict objects, although that seems moderately questionable. OTOH, the code exists and works, so why change that? .update()'s docstring already claims that D.update(E) implies calling E.keys() so it's appropriate not to transform AttributeErrors in PyMapping_Keys() to TypeErrors. Patch eyeballed by Tim.
1 parent 0dcf67e commit 66a0d1d

1 file changed

Lines changed: 70 additions & 17 deletions

File tree

Objects/dictobject.c

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,26 +1047,79 @@ dict_update(register dictobject *mp, PyObject *args)
10471047
register int i;
10481048
dictobject *other;
10491049
dictentry *entry;
1050-
if (!PyArg_Parse(args, "O!", &PyDict_Type, &other))
1050+
PyObject *param;
1051+
/* We accept for the argument either a concrete dictionary object,
1052+
* or an abstract "mapping" object. For the former, we can do
1053+
* things quite efficiently. For the latter, we only require that
1054+
* PyMapping_Keys() and PyObject_GetItem() be supported.
1055+
*/
1056+
if (!PyArg_ParseTuple(args, "O:update", &param))
10511057
return NULL;
1052-
if (other == mp || other->ma_used == 0)
1053-
goto done; /* a.update(a) or a.update({}); nothing to do */
1054-
/* Do one big resize at the start, rather than incrementally
1055-
resizing as we insert new items. Expect that there will be
1056-
no (or few) overlapping keys. */
1057-
if ((mp->ma_fill + other->ma_used)*3 >= (mp->ma_mask+1)*2) {
1058-
if (dictresize(mp, (mp->ma_used + other->ma_used)*3/2) != 0)
1059-
return NULL;
1058+
1059+
if (PyDict_Check(param)) {
1060+
other = (dictobject*)param;
1061+
if (other == mp || other->ma_used == 0)
1062+
/* a.update(a) or a.update({}); nothing to do */
1063+
goto done;
1064+
/* Do one big resize at the start, rather than
1065+
* incrementally resizing as we insert new items. Expect
1066+
* that there will be no (or few) overlapping keys.
1067+
*/
1068+
if ((mp->ma_fill + other->ma_used)*3 >= (mp->ma_mask+1)*2) {
1069+
if (dictresize(mp, (mp->ma_used + other->ma_used)*3/2) != 0)
1070+
return NULL;
1071+
}
1072+
for (i = 0; i <= other->ma_mask; i++) {
1073+
entry = &other->ma_table[i];
1074+
if (entry->me_value != NULL) {
1075+
Py_INCREF(entry->me_key);
1076+
Py_INCREF(entry->me_value);
1077+
insertdict(mp, entry->me_key, entry->me_hash,
1078+
entry->me_value);
1079+
}
1080+
}
10601081
}
1061-
for (i = 0; i <= other->ma_mask; i++) {
1062-
entry = &other->ma_table[i];
1063-
if (entry->me_value != NULL) {
1064-
Py_INCREF(entry->me_key);
1065-
Py_INCREF(entry->me_value);
1066-
insertdict(mp, entry->me_key, entry->me_hash,
1067-
entry->me_value);
1082+
else {
1083+
/* Do it the generic, slower way */
1084+
PyObject *keys = PyMapping_Keys(param);
1085+
PyObject *iter;
1086+
PyObject *key, *value;
1087+
int status;
1088+
1089+
if (keys == NULL)
1090+
/* Docstring says this is equivalent to E.keys() so
1091+
* if E doesn't have a .keys() method we want
1092+
* AttributeError to percolate up. Might as well
1093+
* do the same for any other error.
1094+
*/
1095+
return NULL;
1096+
1097+
iter = PyObject_GetIter(keys);
1098+
Py_DECREF(keys);
1099+
if (iter == NULL)
1100+
return NULL;
1101+
1102+
for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) {
1103+
value = PyObject_GetItem(param, key);
1104+
if (value == NULL) {
1105+
Py_DECREF(iter);
1106+
Py_DECREF(key);
1107+
return NULL;
1108+
}
1109+
status = PyDict_SetItem((PyObject*)mp, key, value);
1110+
Py_DECREF(key);
1111+
Py_DECREF(value);
1112+
if (status < 0) {
1113+
Py_DECREF(iter);
1114+
return NULL;
1115+
}
10681116
}
1117+
Py_DECREF(iter);
1118+
if (PyErr_Occurred())
1119+
/* Iterator completed, via error */
1120+
return NULL;
10691121
}
1122+
10701123
done:
10711124
Py_INCREF(Py_None);
10721125
return Py_None;
@@ -1626,7 +1679,7 @@ static PyMethodDef mapp_methods[] = {
16261679
items__doc__},
16271680
{"values", (PyCFunction)dict_values, METH_OLDARGS,
16281681
values__doc__},
1629-
{"update", (PyCFunction)dict_update, METH_OLDARGS,
1682+
{"update", (PyCFunction)dict_update, METH_VARARGS,
16301683
update__doc__},
16311684
{"clear", (PyCFunction)dict_clear, METH_OLDARGS,
16321685
clear__doc__},

0 commit comments

Comments
 (0)