]> granicus.if.org Git - python/commitdiff
Patch #1686487: you can now pass any mapping after '**' in function calls.
authorGeorg Brandl <georg@python.org>
Mon, 21 May 2007 20:34:16 +0000 (20:34 +0000)
committerGeorg Brandl <georg@python.org>
Mon, 21 May 2007 20:34:16 +0000 (20:34 +0000)
Doc/ref/ref5.tex
Lib/test/output/test_extcall
Lib/test/test_extcall.py
Misc/NEWS
Python/ceval.c

index 17c57d43f57b7a342c17f4cf291da9c58a32d478..73015aa5ca3bbea06ebc1eaa44f9d9fddbb88c9b 100644 (file)
@@ -704,7 +704,7 @@ It is unusual for both keyword arguments and the
 this confusion does not arise.
 
 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
 case of a keyword appearing in both \samp{expression} and as an
 explicit keyword argument, a \exception{TypeError} exception is
index cb93b0d838a5d227f81100b320e4b24c1d7d1dad..a569e531a6ea3df16739ed9bb6fa02517cfee64e 100644 (file)
@@ -9,6 +9,9 @@ test_extcall
 (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}
+(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)
@@ -25,12 +28,12 @@ g() got multiple values for keyword argument 'x'
 g() got multiple values for keyword argument 'b'
 f() keywords must be strings
 h() got an unexpected keyword argument 'e'
-h() argument after * must be a sequence
-dir() argument after * must be a sequence
-NoneType object argument after * must be a sequence
-h() argument after ** must be a dictionary
-dir() argument after ** must be a dictionary
-NoneType object argument after ** must be a dictionary
+h() argument after * must be a sequence, not function
+dir() argument after * must be a sequence, not function
+NoneType object argument after * must be a sequence, not function
+h() argument after ** must be a mapping, not function
+dir() argument after ** must be a mapping, not function
+NoneType object argument after ** must be a mapping, not function
 dir() got multiple values for keyword argument 'b'
 3 512 True
 3
index 284dbb2143a0e708a50127514eeb43af21417ac8..96d1bc184ca9cf5c9405284e76c7151e35c0808a 100644 (file)
@@ -1,5 +1,6 @@
 from test.test_support import verify, verbose, TestFailed, sortdict
 from UserList import UserList
+from UserDict import UserDict
 
 def e(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, 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
 try:
     e(c=3)
index a0692c46e90ff308ab738566f6fc6bac731664a2..f65fece4f6e41ba1d417d848b4e621052ae09ec7 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 1?
 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 E as target:". This is to provide forwards compatibility with
   Python 3.0.
index 4cc158b535d391037074135a77f7cbbbaf597ea1..c547c370870cc9aa80f866ef6c898b5764a80fbb 100644 (file)
@@ -3790,13 +3790,31 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk)
 
        if (flags & CALL_FLAG_KW) {
                kwdict = EXT_POP(*pp_stack);
-               if (!(kwdict && PyDict_Check(kwdict))) {
-                       PyErr_Format(PyExc_TypeError,
-                                    "%s%s argument after ** "
-                                    "must be a dictionary",
-                                    PyEval_GetFuncName(func),
-                                    PyEval_GetFuncDesc(func));
-                       goto ext_call_fail;
+               if (!PyDict_Check(kwdict)) {
+                       PyObject *d;
+                       d = PyDict_New();
+                       if (d == NULL)
+                               goto ext_call_fail;
+                       if (PyDict_Update(d, kwdict) != 0) {
+                               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) {
@@ -3807,10 +3825,11 @@ ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk)
                        if (t == NULL) {
                                if (PyErr_ExceptionMatches(PyExc_TypeError)) {
                                        PyErr_Format(PyExc_TypeError,
-                                                    "%s%s argument after * "
-                                                    "must be a sequence",
+                                                    "%.200s%.200s argument after * "
+                                                    "must be a sequence, not %200s",
                                                     PyEval_GetFuncName(func),
-                                                    PyEval_GetFuncDesc(func));
+                                                    PyEval_GetFuncDesc(func),
+                                                    stararg->ob_type->tp_name);
                                }
                                goto ext_call_fail;
                        }