mirror of
https://github.com/python/cpython.git
synced 2025-07-24 11:44:31 +00:00
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
5 changed files with 126 additions and 55 deletions
|
@ -132,6 +132,12 @@ PyAPI_FUNC(int) PyDict_Merge(PyObject *mp,
|
||||||
int override);
|
int override);
|
||||||
|
|
||||||
#ifndef Py_LIMITED_API
|
#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);
|
PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -259,6 +259,31 @@ not function
|
||||||
...
|
...
|
||||||
TypeError: h() argument after ** must be a mapping, 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)
|
>>> dir(**h)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
|
|
|
@ -46,6 +46,9 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #28257: Improved error message when pass a non-iterable as
|
||||||
a var-positional argument. Added opcode BUILD_TUPLE_UNPACK_WITH_CALL.
|
a var-positional argument. Added opcode BUILD_TUPLE_UNPACK_WITH_CALL.
|
||||||
|
|
||||||
|
|
|
@ -2380,18 +2380,14 @@ Return:
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
PyDict_Update(PyObject *a, PyObject *b)
|
dict_merge(PyObject *a, PyObject *b, int override)
|
||||||
{
|
|
||||||
return PyDict_Merge(a, b, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
PyDict_Merge(PyObject *a, PyObject *b, int override)
|
|
||||||
{
|
{
|
||||||
PyDictObject *mp, *other;
|
PyDictObject *mp, *other;
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i, n;
|
||||||
PyDictKeyEntry *entry, *ep0;
|
PyDictKeyEntry *entry, *ep0;
|
||||||
|
|
||||||
|
assert(0 <= override && override <= 2);
|
||||||
|
|
||||||
/* We accept for the argument either a concrete dictionary object,
|
/* We accept for the argument either a concrete dictionary object,
|
||||||
* or an abstract "mapping" object. For the former, we can do
|
* or an abstract "mapping" object. For the former, we can do
|
||||||
* things quite efficiently. For the latter, we only require that
|
* 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;
|
int err = 0;
|
||||||
Py_INCREF(key);
|
Py_INCREF(key);
|
||||||
Py_INCREF(value);
|
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);
|
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(value);
|
||||||
Py_DECREF(key);
|
Py_DECREF(key);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
|
@ -2472,7 +2474,13 @@ PyDict_Merge(PyObject *a, PyObject *b, int override)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) {
|
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);
|
Py_DECREF(key);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2499,6 +2507,25 @@ PyDict_Merge(PyObject *a, PyObject *b, int override)
|
||||||
return 0;
|
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 *
|
static PyObject *
|
||||||
dict_copy(PyDictObject *mp)
|
dict_copy(PyDictObject *mp)
|
||||||
{
|
{
|
||||||
|
|
102
Python/ceval.c
102
Python/ceval.c
|
@ -2710,9 +2710,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
TARGET(BUILD_MAP_UNPACK_WITH_CALL)
|
|
||||||
TARGET(BUILD_MAP_UNPACK) {
|
TARGET(BUILD_MAP_UNPACK) {
|
||||||
int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL;
|
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
PyObject *sum = PyDict_New();
|
PyObject *sum = PyDict_New();
|
||||||
if (sum == NULL)
|
if (sum == NULL)
|
||||||
|
@ -2720,53 +2718,10 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
|
|
||||||
for (i = oparg; i > 0; i--) {
|
for (i = oparg; i > 0; i--) {
|
||||||
PyObject *arg = PEEK(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 (PyDict_Update(sum, arg) < 0) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"'%.200s' object is not a mapping",
|
"'%.200s' object is not a mapping1",
|
||||||
arg->ob_type->tp_name);
|
arg->ob_type->tp_name);
|
||||||
}
|
}
|
||||||
Py_DECREF(sum);
|
Py_DECREF(sum);
|
||||||
|
@ -2780,6 +2735,61 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
DISPATCH();
|
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) {
|
TARGET(MAP_ADD) {
|
||||||
PyObject *key = TOP();
|
PyObject *key = TOP();
|
||||||
PyObject *value = SECOND();
|
PyObject *value = SECOND();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue