Patch #1686487: you can now pass any mapping after '**' in function calls.

This commit is contained in:
Georg Brandl 2007-05-21 20:34:16 +00:00
parent 70f996be24
commit 2134e754f2
5 changed files with 49 additions and 17 deletions

View File

@ -704,7 +704,7 @@ It is unusual for both keyword arguments and the
this confusion does not arise. this confusion does not arise.
If the syntax \samp{**expression} appears in the function call, If the syntax \samp{**expression} appears in the function call,
\samp{expression} must evaluate to a (subclass of) dictionary, the \samp{expression} must evaluate to a mapping, the
contents of which are treated as additional keyword arguments. In the contents of which are treated as additional keyword arguments. In the
case of a keyword appearing in both \samp{expression} and as an case of a keyword appearing in both \samp{expression} and as an
explicit keyword argument, a \exception{TypeError} exception is explicit keyword argument, a \exception{TypeError} exception is

View File

@ -9,6 +9,9 @@ test_extcall
(1, 2, 3) {'a': 4, 'b': 5} (1, 2, 3) {'a': 4, 'b': 5}
(1, 2, 3, 4, 5) {'a': 6, 'b': 7} (1, 2, 3, 4, 5) {'a': 6, 'b': 7}
(1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5} (1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
(1, 2, 3) {'a': 4, 'b': 5}
(1, 2, 3, 4, 5) {'a': 6, 'b': 7}
(1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
TypeError: g() takes at least 1 argument (0 given) TypeError: g() takes at least 1 argument (0 given)
TypeError: g() takes at least 1 argument (0 given) TypeError: g() takes at least 1 argument (0 given)
TypeError: g() takes at least 1 argument (0 given) TypeError: g() takes at least 1 argument (0 given)
@ -25,12 +28,12 @@ g() got multiple values for keyword argument 'x'
g() got multiple values for keyword argument 'b' g() got multiple values for keyword argument 'b'
f() keywords must be strings f() keywords must be strings
h() got an unexpected keyword argument 'e' h() got an unexpected keyword argument 'e'
h() argument after * must be a sequence h() argument after * must be a sequence, not function
dir() argument after * must be a sequence dir() argument after * must be a sequence, not function
NoneType object argument after * must be a sequence NoneType object argument after * must be a sequence, not function
h() argument after ** must be a dictionary h() argument after ** must be a mapping, not function
dir() argument after ** must be a dictionary dir() argument after ** must be a mapping, not function
NoneType object argument after ** must be a dictionary NoneType object argument after ** must be a mapping, not function
dir() got multiple values for keyword argument 'b' dir() got multiple values for keyword argument 'b'
3 512 True 3 512 True
3 3

View File

@ -1,5 +1,6 @@
from test.test_support import verify, verbose, TestFailed, sortdict from test.test_support import verify, verbose, TestFailed, sortdict
from UserList import UserList from UserList import UserList
from UserDict import UserDict
def e(a, b): def e(a, b):
print a, b print a, b
@ -25,6 +26,12 @@ f(1, 2, 3, **{'a':4, 'b':5})
f(1, 2, 3, *(4, 5), **{'a':6, 'b':7}) f(1, 2, 3, *(4, 5), **{'a':6, 'b':7})
f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b':9}) f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b':9})
f(1, 2, 3, **UserDict(a=4, b=5))
f(1, 2, 3, *(4, 5), **UserDict(a=6, b=7))
f(1, 2, 3, x=4, y=5, *(6, 7), **UserDict(a=8, b=9))
# Verify clearing of SF bug #733667 # Verify clearing of SF bug #733667
try: try:
e(c=3) e(c=3)

View File

@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 1?
Core and builtins Core and builtins
----------------- -----------------
- Patch #1686487: you can now pass any mapping after '**' in function
calls.
- except clauses may now be spelled either "except E, target:" or - except clauses may now be spelled either "except E, target:" or
"except E as target:". This is to provide forwards compatibility with "except E as target:". This is to provide forwards compatibility with
Python 3.0. Python 3.0.

View File

@ -3790,13 +3790,31 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk)
if (flags & CALL_FLAG_KW) { if (flags & CALL_FLAG_KW) {
kwdict = EXT_POP(*pp_stack); kwdict = EXT_POP(*pp_stack);
if (!(kwdict && PyDict_Check(kwdict))) { if (!PyDict_Check(kwdict)) {
PyErr_Format(PyExc_TypeError, PyObject *d;
"%s%s argument after ** " d = PyDict_New();
"must be a dictionary", if (d == NULL)
PyEval_GetFuncName(func), goto ext_call_fail;
PyEval_GetFuncDesc(func)); if (PyDict_Update(d, kwdict) != 0) {
goto ext_call_fail; Py_DECREF(d);
/* PyDict_Update raises attribute
* error (percolated from an attempt
* to get 'keys' attribute) instead of
* a type error if its second argument
* is not a mapping.
*/
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),
kwdict->ob_type->tp_name);
}
goto ext_call_fail;
}
Py_DECREF(kwdict);
kwdict = d;
} }
} }
if (flags & CALL_FLAG_VAR) { if (flags & CALL_FLAG_VAR) {
@ -3807,10 +3825,11 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk)
if (t == NULL) { if (t == NULL) {
if (PyErr_ExceptionMatches(PyExc_TypeError)) { if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"%s%s argument after * " "%.200s%.200s argument after * "
"must be a sequence", "must be a sequence, not %200s",
PyEval_GetFuncName(func), PyEval_GetFuncName(func),
PyEval_GetFuncDesc(func)); PyEval_GetFuncDesc(func),
stararg->ob_type->tp_name);
} }
goto ext_call_fail; goto ext_call_fail;
} }