cpython: 148172f75d43 (original) (raw)
Mercurial > cpython
changeset 104233:148172f75d43 3.6
Issue #27358: Optimized merging var-keyword arguments and improved error message when pass a non-mapping as a var-keyword argument. [#27358]
Serhiy Storchaka storchaka@gmail.com | |
---|---|
date | Sun, 02 Oct 2016 11:06:43 +0300 |
parents | d619246e5eb7 |
children | 489ad68b35c0 872019fd3147 |
files | Include/dictobject.h Lib/test/test_extcall.py Misc/NEWS Objects/dictobject.c Python/ceval.c |
diffstat | 5 files changed, 127 insertions(+), 56 deletions(-)[+] [-] Include/dictobject.h 6 Lib/test/test_extcall.py 25 Misc/NEWS 3 Objects/dictobject.c 45 Python/ceval.c 104 |
line wrap: on
line diff
--- a/Include/dictobject.h +++ b/Include/dictobject.h @@ -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);
--- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -259,6 +259,31 @@ not function ... TypeError: h() argument after ** must be a mapping, not function
- Traceback (most recent call last):
...[](#l2.9)
- TypeError: h() argument after ** must be a mapping, not list
- Traceback (most recent call last):
...[](#l2.14)
- TypeError: h() argument after ** must be a mapping, not function
- Traceback (most recent call last):
...[](#l2.19)
- TypeError: h() argument after ** must be a mapping, not list
- Traceback (most recent call last):
...[](#l2.24)
- TypeError: h() argument after ** must be a mapping, not function
- Traceback (most recent call last):
...[](#l2.29)
- TypeError: h() argument after ** must be a mapping, not list
+ >>> dir(**h) Traceback (most recent call last): ...
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -46,6 +46,9 @@ Core and Builtins Library ------- +- Issue #27358: Optimized merging var-keyword arguments and improved error
- Issue #28257: Improved error message when pass a non-iterable as a var-positional argument. Added opcode BUILD_TUPLE_UNPACK_WITH_CALL.
--- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2380,18 +2380,14 @@ Return: } int -PyDict_Update(PyObject *a, PyObject *b) -{
-} - -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;
+ /* 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, i int err = 0; Py_INCREF(key); Py_INCREF(value);
if (override || PyDict_GetItem(a, key) == NULL)[](#l4.29)
if (override == 1 || _PyDict_GetItem_KnownHash(a, key, hash) == NULL)[](#l4.30) err = insertdict(mp, key, hash, value);[](#l4.31)
else if (override != 0) {[](#l4.32)
_PyErr_SetKeyError(key);[](#l4.33)
Py_DECREF(value);[](#l4.34)
Py_DECREF(key);[](#l4.35)
return -1;[](#l4.36)
}[](#l4.37) Py_DECREF(value);[](#l4.38) Py_DECREF(key);[](#l4.39) if (err != 0)[](#l4.40)
@@ -2472,7 +2474,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) {[](#l4.45)
if (override != 1 && PyDict_GetItem(a, key) != NULL) {[](#l4.46)
if (override != 0) {[](#l4.47)
_PyErr_SetKeyError(key);[](#l4.48)
Py_DECREF(key);[](#l4.49)
Py_DECREF(iter);[](#l4.50)
return -1;[](#l4.51)
}[](#l4.52) Py_DECREF(key);[](#l4.53) continue;[](#l4.54) }[](#l4.55)
@@ -2499,6 +2507,25 @@ PyDict_Merge(PyObject *a, PyObject *b, i return 0; } +int +PyDict_Update(PyObject *a, PyObject *b) +{
+} + +int +PyDict_Merge(PyObject *a, PyObject *b, int override) +{
+} + +int +_PyDict_MergeEx(PyObject *a, PyObject *b, int override) +{
+} + static PyObject * dict_copy(PyDictObject *mp) {
--- a/Python/ceval.c +++ b/Python/ceval.c @@ -2710,9 +2710,32 @@ PyObject * DISPATCH(); }
TARGET(BUILD_MAP_UNPACK_WITH_CALL)[](#l5.7) TARGET(BUILD_MAP_UNPACK) {[](#l5.8)
int with_call = opcode == BUILD_MAP_UNPACK_WITH_CALL;[](#l5.9)
Py_ssize_t i;[](#l5.10)
PyObject *sum = PyDict_New();[](#l5.11)
if (sum == NULL)[](#l5.12)
goto error;[](#l5.13)
for (i = oparg; i > 0; i--) {[](#l5.15)
PyObject *arg = PEEK(i);[](#l5.16)
if (PyDict_Update(sum, arg) < 0) {[](#l5.17)
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {[](#l5.18)
PyErr_Format(PyExc_TypeError,[](#l5.19)
"'%.200s' object is not a mapping1",[](#l5.20)
arg->ob_type->tp_name);[](#l5.21)
}[](#l5.22)
Py_DECREF(sum);[](#l5.23)
goto error;[](#l5.24)
}[](#l5.25)
}[](#l5.26)
while (oparg--)[](#l5.28)
Py_DECREF(POP());[](#l5.29)
PUSH(sum);[](#l5.30)
DISPATCH();[](#l5.31)
}[](#l5.32)
TARGET(BUILD_MAP_UNPACK_WITH_CALL) {[](#l5.34) Py_ssize_t i;[](#l5.35) PyObject *sum = PyDict_New();[](#l5.36) if (sum == NULL)[](#l5.37)
@@ -2720,55 +2743,42 @@ PyObject * for (i = oparg; i > 0; i--) { PyObject *arg = PEEK(i);
if (with_call && PyDict_Size(sum)) {[](#l5.42)
PyObject *intersection = _PyDictView_Intersect(sum, arg);[](#l5.43)
if (intersection == NULL) {[](#l5.45)
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {[](#l5.46)
PyObject *func = PEEK(2 + oparg);[](#l5.47)
PyErr_Format(PyExc_TypeError,[](#l5.48)
"%.200s%.200s argument after ** "[](#l5.49)
"must be a mapping, not %.200s",[](#l5.50)
PyEval_GetFuncName(func),[](#l5.51)
PyEval_GetFuncDesc(func),[](#l5.52)
arg->ob_type->tp_name);[](#l5.53)
}[](#l5.54)
Py_DECREF(sum);[](#l5.55)
goto error;[](#l5.56)
}[](#l5.57)
if (PySet_GET_SIZE(intersection)) {[](#l5.59)
Py_ssize_t idx = 0;[](#l5.60)
PyObject *key;[](#l5.61)
PyObject *func = PEEK(2 + oparg);[](#l5.62)
Py_hash_t hash;[](#l5.63)
_PySet_NextEntry(intersection, &idx, &key, &hash);[](#l5.64)
if (!PyUnicode_Check(key)) {[](#l5.65)
PyErr_Format(PyExc_TypeError,[](#l5.66)
"%.200s%.200s keywords must be strings",[](#l5.67)
PyEval_GetFuncName(func),[](#l5.68)
PyEval_GetFuncDesc(func));[](#l5.69)
} else {[](#l5.70)
PyErr_Format(PyExc_TypeError,[](#l5.71)
"%.200s%.200s got multiple "[](#l5.72)
"values for keyword argument '%U'",[](#l5.73)
PyEval_GetFuncName(func),[](#l5.74)
PyEval_GetFuncDesc(func),[](#l5.75)
key);[](#l5.76)
}[](#l5.77)
Py_DECREF(intersection);[](#l5.78)
Py_DECREF(sum);[](#l5.79)
goto error;[](#l5.80)
}[](#l5.81)
Py_DECREF(intersection);[](#l5.82)
}[](#l5.83)
if (PyDict_Update(sum, arg) < 0) {[](#l5.85)
if (_PyDict_MergeEx(sum, arg, 2) < 0) {[](#l5.86)
PyObject *func = PEEK(2 + oparg);[](#l5.87) if (PyErr_ExceptionMatches(PyExc_AttributeError)) {[](#l5.88) PyErr_Format(PyExc_TypeError,[](#l5.89)
"'%.200s' object is not a mapping",[](#l5.90)
"%.200s%.200s argument after ** "[](#l5.91)
"must be a mapping, not %.200s",[](#l5.92)
PyEval_GetFuncName(func),[](#l5.93)
PyEval_GetFuncDesc(func),[](#l5.94) arg->ob_type->tp_name);[](#l5.95) }[](#l5.96)
else if (PyErr_ExceptionMatches(PyExc_KeyError)) {[](#l5.97)
PyObject *exc, *val, *tb;[](#l5.98)
PyErr_Fetch(&exc, &val, &tb);[](#l5.99)
if (val && PyTuple_Check(val) && PyTuple_GET_SIZE(val) == 1) {[](#l5.100)
PyObject *key = PyTuple_GET_ITEM(val, 0);[](#l5.101)
if (!PyUnicode_Check(key)) {[](#l5.102)
PyErr_Format(PyExc_TypeError,[](#l5.103)
"%.200s%.200s keywords must be strings",[](#l5.104)
PyEval_GetFuncName(func),[](#l5.105)
PyEval_GetFuncDesc(func));[](#l5.106)
} else {[](#l5.107)
PyErr_Format(PyExc_TypeError,[](#l5.108)
"%.200s%.200s got multiple "[](#l5.109)
"values for keyword argument '%U'",[](#l5.110)
PyEval_GetFuncName(func),[](#l5.111)
PyEval_GetFuncDesc(func),[](#l5.112)
key);[](#l5.113)
}[](#l5.114)
Py_XDECREF(exc);[](#l5.115)
Py_XDECREF(val);[](#l5.116)
Py_XDECREF(tb);[](#l5.117)
}[](#l5.118)
else {[](#l5.119)
PyErr_Restore(exc, val, tb);[](#l5.120)
}[](#l5.121)
}[](#l5.122) Py_DECREF(sum);[](#l5.123) goto error;[](#l5.124) }[](#l5.125)