PyAPI_FUNC(void) _PyEval_FormatAwaitableError(PyThreadState *tstate, PyTypeObject *type, int oparg);
PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc, const char *format_str, PyObject *obj);
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
-PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
+PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs, PyObject *dupkey);
PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) _PyEval_LazyImportName(
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(int) _PyDict_MergeUniq(PyObject *mp, PyObject *other, PyObject **dupkey);
extern void _PyDict_DebugMallocStats(FILE *out);
...
AttributeError: some error
- >>> exc = TypeError('some error')
+ >>> exc = KeyError('some error')
>>> f(**D())
Traceback (most recent call last):
...
- TypeError: some error
+ KeyError: 'some error'
"""
def test_errors_in_keys_next():
...
AttributeError: some error
- >>> exc = TypeError('some error')
+ >>> exc = KeyError('some error')
>>> f(**D())
Traceback (most recent call last):
...
- TypeError: some error
+ KeyError: 'some error'
"""
def test_errors_in_getitem():
...
AttributeError: some error
- >>> exc = TypeError('some error')
+ >>> exc = KeyError('some error')
>>> f(**D())
Traceback (most recent call last):
...
- TypeError: some error
+ KeyError: 'some error'
"""
import doctest
...
AttributeError: some error
- >>> exc = TypeError('some error')
+ >>> exc = KeyError('some error')
>>> {**D()}
Traceback (most recent call last):
...
- TypeError: some error
+ KeyError: 'some error'
"""
def test_errors_in_keys_next():
...
AttributeError: some error
- >>> exc = TypeError('some error')
+ >>> exc = KeyError('some error')
>>> {**D()}
Traceback (most recent call last):
...
- TypeError: some error
+ KeyError: 'some error'
"""
def test_errors_in_getitem():
...
AttributeError: some error
- >>> exc = TypeError('some error')
+ >>> exc = KeyError('some error')
>>> {**D()}
Traceback (most recent call last):
...
- TypeError: some error
+ KeyError: 'some error'
"""
__test__ = {'doctests' : doctests}
-:exc:`AttributeError`\ s raised in :meth:`!keys` or :meth:`!__getitem__`
+:exc:`AttributeError`\ s and :exc:`KeyError`\ s raised in :meth:`!keys` or :meth:`!__getitem__`
during dictionary unpacking (``{**mymapping}`` or ``func(**mymapping)``) are
no longer masked by :exc:`TypeError`.
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict);
PyObject *update_o = PyStackRef_AsPyObjectBorrow(update);
+ PyObject *dupkey = NULL;
_PyFrame_SetStackPointer(frame, stack_pointer);
- int err = _PyDict_MergeEx(dict_o, update_o, 2);
+ int err = _PyDict_MergeUniq(dict_o, update_o, &dupkey);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err < 0) {
_PyFrame_SetStackPointer(frame, stack_pointer);
- _PyEval_FormatKwargsError(tstate, callable_o, update_o);
+ _PyEval_FormatKwargsError(tstate, callable_o, update_o, dupkey);
+ Py_XDECREF(dupkey);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(error);
}
static PyObject* frozendict_new(PyTypeObject *type, PyObject *args,
PyObject *kwds);
static PyObject* dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
-static int dict_merge(PyObject *a, PyObject *b, int override);
+static int dict_merge(PyObject *a, PyObject *b, int override, PyObject **dupkey);
static int dict_contains(PyObject *op, PyObject *key);
static int dict_merge_from_seq2(PyObject *d, PyObject *seq2, int override);
Py_DECREF(d);
return NULL;
}
- if (dict_merge(copy, d, 1) < 0) {
+ if (dict_merge(copy, d, 1, NULL) < 0) {
Py_DECREF(d);
Py_DECREF(copy);
return NULL;
dict_update_arg(PyObject *self, PyObject *arg)
{
if (PyAnyDict_CheckExact(arg)) {
- return dict_merge(self, arg, 1);
+ return dict_merge(self, arg, 1, NULL);
}
int has_keys = PyObject_HasAttrWithError(arg, &_Py_ID(keys));
if (has_keys < 0) {
return -1;
}
if (has_keys) {
- return dict_merge(self, arg, 1);
+ return dict_merge(self, arg, 1, NULL);
}
return dict_merge_from_seq2(self, arg, 1);
}
if (result == 0 && kwds != NULL) {
if (PyArg_ValidateKeywordArguments(kwds))
- result = dict_merge(self, kwds, 1);
+ result = dict_merge(self, kwds, 1, NULL);
else
result = -1;
}
}
static int
-dict_dict_merge(PyDictObject *mp, PyDictObject *other, int override)
+dict_dict_merge(PyDictObject *mp, PyDictObject *other, int override, PyObject **dupkey)
{
assert(can_modify_dict(mp));
ASSERT_DICT_LOCKED(other);
/* a.update(a) or a.update({}); nothing to do */
return 0;
if (mp->ma_used == 0) {
- /* Since the target dict is empty, PyDict_GetItem()
- * always returns NULL. Setting override to 1
- * skips the unnecessary test.
- */
+ /* Since the target dict is empty, _PyDict_Contains_KnownHash()
+ * always returns 0. Setting override to 1
+ * skips the unnecessary test.
+ */
override = 1;
PyDictKeysObject *okeys = other->ma_keys;
err = insertdict(mp, Py_NewRef(key), hash, Py_NewRef(value));
}
else if (err > 0) {
- if (override != 0) {
- _PyErr_SetKeyError(key);
+ if (dupkey != NULL) {
+ *dupkey = key;
Py_DECREF(value);
- Py_DECREF(key);
- return -1;
+ return -2;
}
err = 0;
}
}
static int
-dict_merge(PyObject *a, PyObject *b, int override)
+dict_merge(PyObject *a, PyObject *b, int override, PyObject **dupkey)
{
assert(a != NULL);
assert(b != NULL);
PyDictObject *other = (PyDictObject*)b;
int res;
Py_BEGIN_CRITICAL_SECTION2(a, b);
- res = dict_dict_merge((PyDictObject *)a, other, override);
+ res = dict_dict_merge((PyDictObject *)a, other, override, dupkey);
ASSERT_CONSISTENT(a);
Py_END_CRITICAL_SECTION2();
return res;
status = dict_contains(a, key);
if (status != 0) {
if (status > 0) {
- if (override == 0) {
+ if (dupkey == NULL) {
Py_DECREF(key);
continue;
}
- _PyErr_SetKeyError(key);
+ *dupkey = key;
+ res = -2;
+ }
+ else {
+ Py_DECREF(key);
+ res = -1;
}
- Py_DECREF(key);
Py_DECREF(iter);
- res = -1;
goto slow_exit;
}
}
}
static int
-dict_merge_api(PyObject *a, PyObject *b, int override)
+dict_merge_api(PyObject *a, PyObject *b, int override, PyObject **dupkey)
{
/* We accept for the argument either a concrete dictionary object,
* or an abstract "mapping" object. For the former, we can do
}
return -1;
}
- return dict_merge(a, b, override);
+ return dict_merge(a, b, override, dupkey);
}
int
PyDict_Update(PyObject *a, PyObject *b)
{
- return dict_merge_api(a, b, 1);
+ return dict_merge_api(a, b, 1, NULL);
}
int
PyDict_Merge(PyObject *a, PyObject *b, int override)
{
/* XXX Deprecate override not in (0, 1). */
- return dict_merge_api(a, b, override != 0);
+ return dict_merge_api(a, b, override != 0, NULL);
}
int
-_PyDict_MergeEx(PyObject *a, PyObject *b, int override)
+_PyDict_MergeUniq(PyObject *a, PyObject *b, PyObject **dupkey)
{
- return dict_merge_api(a, b, override);
+ return dict_merge_api(a, b, 2, dupkey);
}
/*[clinic input]
}
if (copy == NULL)
return NULL;
- if (dict_merge(copy, o, 1) == 0)
+ if (dict_merge(copy, o, 1, NULL) == 0)
return copy;
Py_DECREF(copy);
return NULL;
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict);
PyObject *update_o = PyStackRef_AsPyObjectBorrow(update);
+ PyObject *dupkey = NULL;
- int err = _PyDict_MergeEx(dict_o, update_o, 2);
+ int err = _PyDict_MergeUniq(dict_o, update_o, &dupkey);
if (err < 0) {
- _PyEval_FormatKwargsError(tstate, callable_o, update_o);
+ _PyEval_FormatKwargsError(tstate, callable_o, update_o, dupkey);
+ Py_XDECREF(dupkey);
ERROR_NO_POP();
}
u = update;
}
void
-_PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs)
-{
- /* _PyDict_MergeEx raises attribute
+_PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs, PyObject *dupkey)
+{
+ if (dupkey != NULL) {
+ PyObject *funcstr = _PyObject_FunctionStr(func);
+ _PyErr_Format(
+ tstate, PyExc_TypeError,
+ "%V got multiple values for keyword argument '%S'",
+ funcstr, "finction", dupkey);
+ Py_XDECREF(funcstr);
+ return;
+ }
+ /* _PyDict_MergeUniq raises attribute
* error (percolated from an attempt
* to get 'keys' attribute) instead of
* a type error if its second argument
_PyErr_ChainExceptions1Tstate(tstate, exc);
}
}
- else if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
- PyObject *exc = _PyErr_GetRaisedException(tstate);
- PyObject *args = PyException_GetArgs(exc);
- if (PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1) {
- _PyErr_Clear(tstate);
- PyObject *funcstr = _PyObject_FunctionStr(func);
- if (funcstr != NULL) {
- PyObject *key = PyTuple_GET_ITEM(args, 0);
- _PyErr_Format(
- tstate, PyExc_TypeError,
- "%U got multiple values for keyword argument '%S'",
- funcstr, key);
- Py_DECREF(funcstr);
- }
- Py_XDECREF(exc);
- }
- else {
- _PyErr_SetRaisedException(tstate, exc);
- }
- Py_DECREF(args);
- }
}
void
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict);
PyObject *update_o = PyStackRef_AsPyObjectBorrow(update);
+ PyObject *dupkey = NULL;
stack_pointer[0] = update;
stack_pointer += 1;
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
_PyFrame_SetStackPointer(frame, stack_pointer);
- int err = _PyDict_MergeEx(dict_o, update_o, 2);
+ int err = _PyDict_MergeUniq(dict_o, update_o, &dupkey);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err < 0) {
_PyFrame_SetStackPointer(frame, stack_pointer);
- _PyEval_FormatKwargsError(tstate, callable_o, update_o);
+ _PyEval_FormatKwargsError(tstate, callable_o, update_o, dupkey);
+ Py_XDECREF(dupkey);
stack_pointer = _PyFrame_GetStackPointer(frame);
SET_CURRENT_CACHED_VALUES(0);
JUMP_TO_ERROR();
PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
PyObject *dict_o = PyStackRef_AsPyObjectBorrow(dict);
PyObject *update_o = PyStackRef_AsPyObjectBorrow(update);
+ PyObject *dupkey = NULL;
_PyFrame_SetStackPointer(frame, stack_pointer);
- int err = _PyDict_MergeEx(dict_o, update_o, 2);
+ int err = _PyDict_MergeUniq(dict_o, update_o, &dupkey);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (err < 0) {
_PyFrame_SetStackPointer(frame, stack_pointer);
- _PyEval_FormatKwargsError(tstate, callable_o, update_o);
+ _PyEval_FormatKwargsError(tstate, callable_o, update_o, dupkey);
+ Py_XDECREF(dupkey);
stack_pointer = _PyFrame_GetStackPointer(frame);
JUMP_TO_LABEL(error);
}