diff -r a477ef882a16 Include/dictobject.h --- a/Include/dictobject.h Sat Sep 10 10:45:28 2016 +0000 +++ b/Include/dictobject.h Sun Sep 11 01:51:57 2016 +0300 @@ -132,6 +132,7 @@ PyAPI_FUNC(int) PyDict_Merge(PyObject *m int override); #ifndef Py_LIMITED_API +PyAPI_FUNC(int) _PyDict_MergeEx(PyObject *mp, PyObject *other, int override); PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other); #endif diff -r a477ef882a16 Lib/test/test_extcall.py --- a/Lib/test/test_extcall.py Sat Sep 10 10:45:28 2016 +0000 +++ b/Lib/test/test_extcall.py Sun Sep 11 01:51:57 2016 +0300 @@ -246,17 +246,17 @@ What about willful misconduct? >>> h(**h) Traceback (most recent call last): ... - TypeError: 'function' object is not a mapping + TypeError: h() argument after ** must be a mapping, not function >>> dir(**h) Traceback (most recent call last): ... - TypeError: 'function' object is not a mapping + TypeError: dir() argument after ** must be a mapping, not function >>> None(**h) Traceback (most recent call last): ... - TypeError: 'function' object is not a mapping + TypeError: NoneType object argument after ** must be a mapping, not function >>> dir(b=1, **{'b': 1}) Traceback (most recent call last): diff -r a477ef882a16 Objects/dictobject.c --- a/Objects/dictobject.c Sat Sep 10 10:45:28 2016 +0000 +++ b/Objects/dictobject.c Sun Sep 11 01:51:57 2016 +0300 @@ -2307,10 +2307,19 @@ PyDict_Update(PyObject *a, PyObject *b) int PyDict_Merge(PyObject *a, PyObject *b, int override) { + /* XXX Deprecate override not in (0, 1). */ + return _PyDict_MergeEx(a, b, override != 0); +} + +int +_PyDict_MergeEx(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 @@ -2355,8 +2364,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) @@ -2391,7 +2406,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; } diff -r a477ef882a16 Python/ceval.c --- a/Python/ceval.c Sat Sep 10 10:45:28 2016 +0000 +++ b/Python/ceval.c Sun Sep 11 01:51:57 2016 +0300 @@ -2710,65 +2710,14 @@ 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; - - if (with_call && oparg == 1 && PyDict_CheckExact(TOP())) { - DISPATCH(); - } - - sum = PyDict_New(); + PyObject *sum = PyDict_New(); if (sum == NULL) goto error; 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, @@ -2786,6 +2735,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();