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:
parent
0a3beffc8f
commit
e036ef8fa2
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
...
|
||||
|
|
|
@ -46,6 +46,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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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,12 +2718,34 @@ _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 (PyDict_Update(sum, arg) < 0) {
|
||||
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%.200s' object is not a mapping1",
|
||||
arg->ob_type->tp_name);
|
||||
}
|
||||
Py_DECREF(sum);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
while (oparg--)
|
||||
Py_DECREF(POP());
|
||||
PUSH(sum);
|
||||
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",
|
||||
|
@ -2733,16 +2753,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
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);
|
||||
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",
|
||||
|
@ -2756,18 +2771,13 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
|||
PyEval_GetFuncDesc(func),
|
||||
key);
|
||||
}
|
||||
Py_DECREF(intersection);
|
||||
Py_DECREF(sum);
|
||||
goto error;
|
||||
Py_XDECREF(exc);
|
||||
Py_XDECREF(val);
|
||||
Py_XDECREF(tb);
|
||||
}
|
||||
Py_DECREF(intersection);
|
||||
else {
|
||||
PyErr_Restore(exc, val, tb);
|
||||
}
|
||||
|
||||
if (PyDict_Update(sum, arg) < 0) {
|
||||
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%.200s' object is not a mapping",
|
||||
arg->ob_type->tp_name);
|
||||
}
|
||||
Py_DECREF(sum);
|
||||
goto error;
|
||||
|
|
Loading…
Reference in New Issue