Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 54935) +++ Python/ceval.c (working copy) @@ -3791,13 +3791,31 @@ 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) { @@ -3808,10 +3826,11 @@ 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; } Index: Lib/test/test_extcall.py =================================================================== --- Lib/test/test_extcall.py (revision 54926) +++ Lib/test/test_extcall.py (working copy) @@ -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, *(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: Lib/test/output/test_extcall =================================================================== --- Lib/test/output/test_extcall (revision 54926) +++ Lib/test/output/test_extcall (working copy) @@ -9,6 +9,9 @@ (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 '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: Doc/ref/ref5.tex =================================================================== --- Doc/ref/ref5.tex (revision 54926) +++ Doc/ref/ref5.tex (working copy) @@ -704,7 +704,7 @@ 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