Issue #27358: Optimized merging var-keyword arguments and improved error

message when pass a non-mapping as a var-keyword argument.
This commit is contained in:
Serhiy Storchaka 2016-10-02 11:07:29 +03:00
commit 2e84de3638
5 changed files with 126 additions and 55 deletions

View File

@ -132,6 +132,12 @@ PyAPI_FUNC(int) PyDict_Merge(PyObject *mp,
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

View File

@ -259,6 +259,31 @@ 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
>>> h(a=1, **h)
Traceback (most recent call last):
...
TypeError: h() argument after ** must be a mapping, not function
>>> h(a=1, **[])
Traceback (most recent call last):
...
TypeError: h() argument after ** must be a mapping, not list
>>> h(**{'a': 1}, **h)
Traceback (most recent call last):
...
TypeError: h() argument after ** must be a mapping, not function
>>> h(**{'a': 1}, **[])
Traceback (most recent call last):
...
TypeError: h() argument after ** must be a mapping, not list
>>> dir(**h)
Traceback (most recent call last):
...

View File

@ -54,6 +54,9 @@ Core and Builtins
Library
-------
- Issue #27358: Optimized merging var-keyword arguments and improved error
message when pass a non-mapping as a var-keyword argument.
- Issue #28257: Improved error message when pass a non-iterable as
a var-positional argument. Added opcode BUILD_TUPLE_UNPACK_WITH_CALL.

View File

@ -2380,18 +2380,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
@ -2436,8 +2432,14 @@ PyDict_Merge(PyObject *a, PyObject *b, int override)
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)
@ -2472,7 +2474,13 @@ PyDict_Merge(PyObject *a, PyObject *b, int override)
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;
}
@ -2499,6 +2507,25 @@ PyDict_Merge(PyObject *a, PyObject *b, int override)
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);
}
int
_PyDict_MergeEx(PyObject *a, PyObject *b, int override)
{
return dict_merge(a, b, override);
}
static PyObject *
dict_copy(PyDictObject *mp)
{

View File

@ -2710,9 +2710,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
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)
@ -2720,53 +2718,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
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);
@ -2780,6 +2735,61 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
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();