diff -r 7dd85b19c873 Objects/abstract.c --- a/Objects/abstract.c Mon Aug 22 12:29:42 2016 +0200 +++ b/Objects/abstract.c Mon Aug 22 13:01:42 2016 +0200 @@ -2215,6 +2215,7 @@ PyObject_Call(PyObject *func, PyObject * caller loses its exception */ assert(!PyErr_Occurred()); assert(PyTuple_Check(args)); + assert(args == NULL || PyTuple_Check(args)); assert(kwargs == NULL || PyDict_Check(kwargs)); call = func->ob_type->tp_call; @@ -2268,9 +2269,7 @@ PyObject * assert(func != NULL); assert(nargs >= 0); assert(nargs == 0 || args != NULL); - /* issue #27128: support for keywords will come later: - _PyFunction_FastCall() doesn't support keyword arguments yet */ - assert(kwargs == NULL); + assert(kwargs == NULL || PyDict_Check(kwargs)); if (Py_EnterRecursiveCall(" while calling a Python object")) { return NULL; diff -r 7dd85b19c873 Python/ceval.c --- a/Python/ceval.c Mon Aug 22 12:29:42 2016 +0200 +++ b/Python/ceval.c Mon Aug 22 13:01:42 2016 +0200 @@ -3784,11 +3784,127 @@ too_many_positional(PyCodeObject *co, in PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ +static int +fastlocals_dict(PyObject **fastlocals, PyCodeObject *co, + PyObject *kwdict, PyObject *kwargs) +{ + const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; + _Py_IDENTIFIER(items); + PyObject *items, *iter; + PyObject *item = NULL; + PyObject **co_varnames; + + assert(PyDict_Check(kwargs)); + + if (total_args == 0 && kwdict != NULL) { + if (PyDict_Update(kwdict, kwargs) < 0) { + return -1; + } + return 0; + } + + items = _PyObject_CallMethodId((PyObject *)kwargs, &PyId_items, NULL); + if (items == NULL) { + return -1; + } + + iter = PyObject_GetIter(items); + Py_DECREF(items); + if (iter == NULL) { + goto fail; + } + + co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item; + do { + PyObject *keyword, *value; + int j; + + /* Get first item */ + item = PyIter_Next(iter); + if (item == NULL) { + if (PyErr_Occurred()) { + goto fail; + } + + /* nothing more to add */ + break; + } + assert(PyTuple_CheckExact(item) && PyTuple_GET_SIZE(item) == 2); + + keyword = PyTuple_GET_ITEM(item, 0); + value = PyTuple_GET_ITEM(item, 1); + + if (keyword == NULL || !PyUnicode_Check(keyword)) { + PyErr_Format(PyExc_TypeError, + "%U() keywords must be strings", + co->co_name); + goto fail; + } + + /* Speed hack: do raw pointer compares. As names are + normally interned this should almost always hit. */ + for (j = 0; j < total_args; j++) { + PyObject *name = co_varnames[j]; + if (name == keyword) { + goto kw_found; + } + } + + /* Slow fallback, just in case */ + for (j = 0; j < total_args; j++) { + PyObject *name = co_varnames[j]; + int cmp = PyObject_RichCompareBool(keyword, name, Py_EQ); + if (cmp > 0) { + goto kw_found; + } + else if (cmp < 0) { + goto fail; + } + } + + if (j >= total_args && kwdict == NULL) { + PyErr_Format(PyExc_TypeError, + "%U() got an unexpected keyword argument '%S'", + co->co_name, keyword); + goto fail; + } + + if (PyDict_SetItem(kwdict, keyword, value) == -1) { + goto fail; + } + Py_DECREF(item); + continue; + + kw_found: + if (GETLOCAL(j) != NULL) { + PyErr_Format(PyExc_TypeError, + "%U() got multiple values for argument '%S'", + co->co_name, + keyword); + goto fail; + } + Py_INCREF(value); + SETLOCAL(j, value); + + Py_DECREF(item); + } while (1); + + Py_DECREF(iter); + return 0; + +fail: + Py_DECREF(iter); + Py_XDECREF(item); + return -1; +} + static PyObject * -_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, - PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, - PyObject *name, PyObject *qualname) +_PyEval_EvalCode(PyObject *_co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, + PyObject **kws, int kwcount, PyObject *kwargs, + PyObject **defs, int defcount, + PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; PyFrameObject *f; @@ -3801,6 +3917,9 @@ static PyObject * PyObject *kwdict; assert((kwcount == 0) || (kws != NULL)); + /* kws and kwargs are mutually exclusive + (arbitrary limit to detect bugs) */ + assert(!((kws != NULL) && (kwargs != NULL))); if (globals == NULL) { PyErr_SetString(PyExc_SystemError, @@ -3878,49 +3997,55 @@ static PyObject * normally interned this should almost always hit. */ co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item; for (j = 0; j < total_args; j++) { - PyObject *nm = co_varnames[j]; - if (nm == keyword) + PyObject *name = co_varnames[j]; + if (name == keyword) { goto kw_found; + } } /* Slow fallback, just in case */ for (j = 0; j < total_args; j++) { - PyObject *nm = co_varnames[j]; - int cmp = PyObject_RichCompareBool( - keyword, nm, Py_EQ); - if (cmp > 0) + PyObject *name = co_varnames[j]; + int cmp = PyObject_RichCompareBool(keyword, name, Py_EQ); + if (cmp > 0) { goto kw_found; - else if (cmp < 0) + } + else if (cmp < 0) { goto fail; + } } if (j >= total_args && kwdict == NULL) { PyErr_Format(PyExc_TypeError, - "%U() got an unexpected " - "keyword argument '%S'", + "%U() got an unexpected keyword argument '%S'", + co->co_name, keyword); + goto fail; + } + + if (PyDict_SetItem(kwdict, keyword, value) == -1) { + goto fail; + } + continue; + + kw_found: + if (GETLOCAL(j) != NULL) { + PyErr_Format(PyExc_TypeError, + "%U() got multiple values for argument '%S'", co->co_name, keyword); goto fail; } - - if (PyDict_SetItem(kwdict, keyword, value) == -1) { - goto fail; - } - continue; - - kw_found: - if (GETLOCAL(j) != NULL) { - PyErr_Format(PyExc_TypeError, - "%U() got multiple " - "values for argument '%S'", - co->co_name, - keyword); - goto fail; - } Py_INCREF(value); SETLOCAL(j, value); } + /* Handle keyword arguments (passed as a dictionary) */ + if (kwargs != NULL && PyDict_Size(kwargs) != 0) { + if (fastlocals_dict(fastlocals, co, kwdict, kwargs) < 0) { + goto fail; + } + } + /* Check the number of positional arguments */ if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) { too_many_positional(co, argcount, defcount, fastlocals); @@ -4068,12 +4193,12 @@ PyEval_EvalCodeEx(PyObject *_co, PyObjec PyObject **args, int argcount, PyObject **kws, int kwcount, PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) { - return _PyEval_EvalCodeWithName(_co, globals, locals, - args, argcount, - kws, kwcount, - defs, defcount, - kwdefs, closure, - NULL, NULL); + return _PyEval_EvalCode(_co, globals, locals, + args, argcount, + kws, kwcount, NULL, + defs, defcount, + kwdefs, closure, + NULL, NULL); } static PyObject * @@ -4881,11 +5006,11 @@ fast_function(PyObject *func, PyObject * d = NULL; nd = 0; } - return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, - stack, nargs, - stack + nargs, nk, - d, nd, kwdefs, - closure, name, qualname); + return _PyEval_EvalCode((PyObject*)co, globals, (PyObject *)NULL, + stack, nargs, + stack + nargs, nk, NULL, + d, nd, kwdefs, + closure, name, qualname); } PyObject * @@ -4901,10 +5026,10 @@ PyObject * PCALL(PCALL_FUNCTION); PCALL(PCALL_FAST_FUNCTION); - /* issue #27128: support for keywords will come later */ - assert(kwargs == NULL); - - if (co->co_kwonlyargcount == 0 && kwargs == NULL && + assert(kwargs == NULL || PyDict_Check(kwargs)); + + if (co->co_kwonlyargcount == 0 && + (kwargs == NULL || PyDict_Size(kwargs) == 0) && co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { if (argdefs == NULL && co->co_argcount == nargs) { @@ -4933,11 +5058,11 @@ PyObject * d = NULL; nd = 0; } - return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL, - args, nargs, - NULL, 0, - d, nd, kwdefs, - closure, name, qualname); + return _PyEval_EvalCode((PyObject*)co, globals, (PyObject *)NULL, + args, nargs, + NULL, 0, kwargs, + d, nd, kwdefs, + closure, name, qualname); } static PyObject *