Index: Include/abstract.h =================================================================== --- Include/abstract.h (Revision 53031) +++ Include/abstract.h (Arbeitskopie) @@ -1215,6 +1215,16 @@ /* Implemented as macro: + PyObject *PyMapping_IterKeys(PyObject *o); + + On success, return an iterator of the keys in object o. On + failure, return NULL. This is equivalent to the Python + expression: o.iterkeys(). + */ +#define PyMapping_IterKeys(O) PyObject_CallMethod(O,"iterkeys",NULL) + + /* Implemented as macro: + PyObject *PyMapping_Keys(PyObject *o); On success, return a list of the keys in object o. On Index: Objects/dictobject.c =================================================================== --- Objects/dictobject.c (Revision 53031) +++ Objects/dictobject.c (Arbeitskopie) @@ -1185,7 +1185,8 @@ result = -1; else if (arg != NULL) { - if (PyObject_HasAttrString(arg, "keys")) + if (PyObject_HasAttrString(arg, "iterkeys") || + PyObject_HasAttrString(arg, "keys")) result = PyDict_Merge(self, arg, 1); else result = PyDict_MergeFromSeq2(self, arg, 1); @@ -1299,14 +1300,15 @@ /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that - * PyMapping_Keys() and PyObject_GetItem() be supported. + * (PyMapping_IterKeys() or PyMapping_Keys()) and PyObject_GetItem() + * be supported. */ if (a == NULL || !PyDict_Check(a) || b == NULL) { PyErr_BadInternalCall(); return -1; } mp = (dictobject*)a; - if (PyDict_Check(b)) { + if (PyDict_CheckExact(b)) { other = (dictobject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ @@ -1341,21 +1343,24 @@ } else { /* Do it the generic, slower way */ - PyObject *keys = PyMapping_Keys(b); - PyObject *iter; + PyObject *iter = PyMapping_IterKeys(b); PyObject *key, *value; int status; - if (keys == NULL) - /* Docstring says this is equivalent to E.keys() so - * if E doesn't have a .keys() method we want - * AttributeError to percolate up. Might as well - * do the same for any other error. - */ - return -1; - - iter = PyObject_GetIter(keys); - Py_DECREF(keys); + if (iter == NULL) { + /* might be an old mapping without iterkeys() => try keys() */ + PyErr_Clear(); + PyObject *keys = PyMapping_Keys(b); + if (keys == NULL) + /* Docstring says this is equivalent to E.keys() so + * if E doesn't have a .keys() method we want + * AttributeError to percolate up. Might as well + * do the same for any other error. + */ + return -1; + iter = PyObject_GetIter(keys); + Py_DECREF(keys); + } if (iter == NULL) return -1; Index: Lib/test/test_dict.py =================================================================== --- Lib/test/test_dict.py (Revision 53031) +++ Lib/test/test_dict.py (Arbeitskopie) @@ -136,6 +136,17 @@ d.update(SimpleUserDict()) self.assertEqual(d, {1:1, 2:2, 3:3}) + class SimpleIterUserDict: + def __init__(self): + self.d = {1:1, 2:2, 3:3} + def iterkeys(self): + return iter(self.d) + def __getitem__(self, i): + return self.d[i] + d.clear() + d.update(SimpleIterUserDict()) + self.assertEqual(d, {1:1, 2:2, 3:3}) + class Exc(Exception): pass d.clear()