diff -r b3114b382e87 Include/dictobject.h --- a/Include/dictobject.h Thu Sep 22 11:42:38 2016 +0300 +++ b/Include/dictobject.h Fri Sep 23 23:41:30 2016 +0300 @@ -132,6 +132,12 @@ PyAPI_FUNC(int) PyDict_Merge(PyObject *m int override); #ifndef Py_LIMITED_API +/* Like PyDict_Merge, but override can be 0, 1 or 2. If override is 0, + the first occurrence of a key wins, if override is 1, the last occurrence + of a key wins, if override is 2, a KeyError with conflicting key as + argument is raised. +*/ +PyAPI_FUNC(int) _PyDict_MergeEx(PyObject *mp, PyObject *other, int override); PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other); #endif diff -r b3114b382e87 Lib/test/test_extcall.py --- a/Lib/test/test_extcall.py Thu Sep 22 11:42:38 2016 +0300 +++ b/Lib/test/test_extcall.py Fri Sep 23 23:41:30 2016 +0300 @@ -249,6 +249,11 @@ not function ... TypeError: h() argument after ** must be a mapping, not function + >>> h(**[]) + Traceback (most recent call last): + ... + TypeError: h() argument after ** must be a mapping, not list + >>> dir(**h) Traceback (most recent call last): ... diff -r b3114b382e87 Objects/dictobject.c --- a/Objects/dictobject.c Thu Sep 22 11:42:38 2016 +0300 +++ b/Objects/dictobject.c Fri Sep 23 23:41:30 2016 +0300 @@ -2379,18 +2379,14 @@ Return: } int -PyDict_Update(PyObject *a, PyObject *b) -{ - return PyDict_Merge(a, b, 1); -} - -int -PyDict_Merge(PyObject *a, PyObject *b, int override) +dict_merge(PyObject *a, PyObject *b, int override) { PyDictObject *mp, *other; Py_ssize_t i, n; PyDictKeyEntry *entry, *ep0; + assert(0 <= override && override <= 2); + /* 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 @@ -2435,8 +2431,14 @@ PyDict_Merge(PyObject *a, PyObject *b, i int err = 0; Py_INCREF(key); Py_INCREF(value); - if (override || PyDict_GetItem(a, key) == NULL) + if (override == 1 || _PyDict_GetItem_KnownHash(a, key, hash) == NULL) err = insertdict(mp, key, hash, value); + else if (override != 0) { + _PyErr_SetKeyError(key); + Py_DECREF(value); + Py_DECREF(key); + return -1; + } Py_DECREF(value); Py_DECREF(key); if (err != 0) @@ -2471,7 +2473,13 @@ PyDict_Merge(PyObject *a, PyObject *b, i return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { - if (!override && PyDict_GetItem(a, key) != NULL) { + if (override != 1 && PyDict_GetItem(a, key) != NULL) { + if (override != 0) { + _PyErr_SetKeyError(key); + Py_DECREF(key); + Py_DECREF(iter); + return -1; + } Py_DECREF(key); continue; } @@ -2498,6 +2506,30 @@ PyDict_Merge(PyObject *a, PyObject *b, i return 0; } +int +PyDict_Update(PyObject *a, PyObject *b) +{ + return dict_merge(a, b, 1); +} + +int +PyDict_Merge(PyObject *a, PyObject *b, int override) +{ + /* XXX Deprecate override not in (0, 1). */ + return dict_merge(a, b, override != 0); +} + +/* If override is 1, existing pairs in a will be replaced if a matching key + is found in b, if override is 0, pairs will only be added if there is not + a matching key in a, if override is 2, will raise a KeyError if there is + a matching key in a and b, with passing this key as argument. + */ +int +_PyDict_MergeEx(PyObject *a, PyObject *b, int override) +{ + return dict_merge(a, b, override); +} + static PyObject * dict_copy(PyDictObject *mp) { diff -r b3114b382e87 Python/ceval.c --- a/Python/ceval.c Thu Sep 22 11:42:38 2016 +0300 +++ b/Python/ceval.c Fri Sep 23 23:41:30 2016 +0300 @@ -2699,9 +2699,7 @@ PyObject * DISPATCH(); } - TARGET(BUILD_MAP_UNPACK_WITH_CALL) TARGET(BUILD_MAP_UNPACK) { - int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL; Py_ssize_t i; PyObject *sum = PyDict_New(); if (sum == NULL) @@ -2709,53 +2707,10 @@ PyObject * for (i = oparg; i > 0; i--) { PyObject *arg = PEEK(i); - if (with_call && PyDict_Size(sum)) { - PyObject *intersection = _PyDictView_Intersect(sum, arg); - - if (intersection == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyObject *func = PEEK(2 + oparg); - PyErr_Format(PyExc_TypeError, - "%.200s%.200s argument after ** " - "must be a mapping, not %.200s", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func), - arg->ob_type->tp_name); - } - Py_DECREF(sum); - goto error; - } - - if (PySet_GET_SIZE(intersection)) { - Py_ssize_t idx = 0; - PyObject *key; - PyObject *func = PEEK(2 + oparg); - Py_hash_t hash; - _PySet_NextEntry(intersection, &idx, &key, &hash); - if (!PyUnicode_Check(key)) { - PyErr_Format(PyExc_TypeError, - "%.200s%.200s keywords must be strings", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func)); - } else { - PyErr_Format(PyExc_TypeError, - "%.200s%.200s got multiple " - "values for keyword argument '%U'", - PyEval_GetFuncName(func), - PyEval_GetFuncDesc(func), - key); - } - Py_DECREF(intersection); - Py_DECREF(sum); - goto error; - } - Py_DECREF(intersection); - } - if (PyDict_Update(sum, arg) < 0) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Format(PyExc_TypeError, - "'%.200s' object is not a mapping", + "'%.200s' object is not a mapping1", arg->ob_type->tp_name); } Py_DECREF(sum); @@ -2769,6 +2724,61 @@ PyObject * DISPATCH(); } + TARGET(BUILD_MAP_UNPACK_WITH_CALL) { + Py_ssize_t i; + PyObject *sum = PyDict_New(); + if (sum == NULL) + goto error; + + for (i = oparg; i > 0; i--) { + PyObject *arg = PEEK(i); + if (_PyDict_MergeEx(sum, arg, 2) < 0) { + PyObject *func = PEEK(2 + oparg); + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s argument after ** " + "must be a mapping, not %.200s", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + arg->ob_type->tp_name); + } + else if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (val && PyTuple_Check(val) && PyTuple_GET_SIZE(val) == 1) { + PyObject *key = PyTuple_GET_ITEM(val, 0); + if (!PyUnicode_Check(key)) { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s keywords must be strings", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); + } else { + PyErr_Format(PyExc_TypeError, + "%.200s%.200s got multiple " + "values for keyword argument '%U'", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + key); + } + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + } + else { + PyErr_Restore(exc, val, tb); + } + } + Py_DECREF(sum); + goto error; + } + } + + while (oparg--) + Py_DECREF(POP()); + PUSH(sum); + DISPATCH(); + } + TARGET(MAP_ADD) { PyObject *key = TOP(); PyObject *value = SECOND();