diff -r ce6a6cc3765d Python/getargs.c --- a/Python/getargs.c Tue Dec 20 22:52:33 2016 +0800 +++ b/Python/getargs.c Tue Dec 20 22:58:58 2016 +0200 @@ -1591,17 +1591,17 @@ PyArg_ValidateKeywordArguments(PyObject #define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':') static int -vgetargskeywords(PyObject *args, PyObject *keywords, const char *format, +vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, char **kwlist, va_list *p_va, int flags) { char msgbuf[512]; int levels[32]; - const char *fname, *msg, *custom_msg, *keyword; + const char *fname, *msg, *custom_msg; int min = INT_MAX; int max = INT_MAX; int i, pos, len; int skip = 0; - Py_ssize_t nargs, nkeywords; + Py_ssize_t nargs, nkwargs; PyObject *current_arg; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelist_t freelist; @@ -1611,7 +1611,7 @@ vgetargskeywords(PyObject *args, PyObjec freelist.entries_malloced = 0; assert(args != NULL && PyTuple_Check(args)); - assert(keywords == NULL || PyDict_Check(keywords)); + assert(kwargs == NULL || PyDict_Check(kwargs)); assert(format != NULL); assert(kwlist != NULL); assert(p_va != NULL); @@ -1650,21 +1650,20 @@ vgetargskeywords(PyObject *args, PyObjec } nargs = PyTuple_GET_SIZE(args); - nkeywords = (keywords == NULL) ? 0 : PyDict_GET_SIZE(keywords); - if (nargs + nkeywords > len) { + nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs); + if (nargs + nkwargs > len) { PyErr_Format(PyExc_TypeError, "%s%s takes at most %d argument%s (%zd given)", (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", len, (len == 1) ? "" : "s", - nargs + nkeywords); + nargs + nkwargs); return cleanreturn(0, &freelist); } /* convert tuple args and keyword args in same loop, using kwlist to drive process */ for (i = 0; i < len; i++) { - keyword = kwlist[i]; if (*format == '|') { if (min != INT_MAX) { PyErr_SetString(PyExc_SystemError, @@ -1718,26 +1717,17 @@ vgetargskeywords(PyObject *args, PyObjec return cleanreturn(0, &freelist); } if (!skip) { - current_arg = NULL; - if (nkeywords && i >= pos) { - current_arg = PyDict_GetItemString(keywords, keyword); - if (!current_arg && PyErr_Occurred()) { - return cleanreturn(0, &freelist); - } + if (i < nargs) { + current_arg = PyTuple_GET_ITEM(args, i); } - if (current_arg) { - --nkeywords; - if (i < nargs) { - /* arg present in tuple and in dict */ - PyErr_Format(PyExc_TypeError, - "Argument given by name ('%s') " - "and position (%d)", - keyword, i+1); - return cleanreturn(0, &freelist); - } + else if (nkwargs && i >= pos) { + current_arg = PyDict_GetItemString(kwargs, kwlist[i]); + if (current_arg) + --nkwargs; } - else if (i < nargs) - current_arg = PyTuple_GET_ITEM(args, i); + else { + current_arg = NULL; + } if (current_arg) { msg = convertitem(current_arg, &format, p_va, flags, @@ -1761,8 +1751,8 @@ vgetargskeywords(PyObject *args, PyObjec } else { PyErr_Format(PyExc_TypeError, "Required argument " - "'%s' (pos %d) not found", - keyword, i+1); + "'%s' (pos %d) not found", + kwlist[i], i+1); return cleanreturn(0, &freelist); } } @@ -1770,7 +1760,7 @@ vgetargskeywords(PyObject *args, PyObjec * fulfilled and no keyword args left, with no further * validation. XXX Maybe skip this in debug build ? */ - if (!nkeywords && !skip) { + if (!nkwargs && !skip) { return cleanreturn(1, &freelist); } } @@ -1801,19 +1791,32 @@ vgetargskeywords(PyObject *args, PyObjec return cleanreturn(0, &freelist); } - /* make sure there are no extraneous keyword arguments */ - if (nkeywords > 0) { - PyObject *key, *value; - Py_ssize_t pos = 0; - while (PyDict_Next(keywords, &pos, &key, &value)) { + if (nkwargs > 0) { + PyObject *key; + Py_ssize_t j; + /* make sure there are no arguments given by name and position */ + for (i = pos; i < nargs; i++) { + current_arg = PyDict_GetItemString(kwargs, kwlist[i]); + if (current_arg) { + /* arg present in tuple and in dict */ + PyErr_Format(PyExc_TypeError, + "Argument given by name ('%s') " + "and position (%d)", + kwlist[i], i+1); + return cleanreturn(0, &freelist); + } + } + /* make sure there are no extraneous keyword arguments */ + j = 0; + while (PyDict_Next(kwargs, &j, &key, NULL)) { int match = 0; if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, "keywords must be strings"); return cleanreturn(0, &freelist); } - for (i = 0; i < len; i++) { - if (*kwlist[i] && _PyUnicode_EqualToASCIIString(key, kwlist[i])) { + for (i = pos; i < len; i++) { + if (_PyUnicode_EqualToASCIIString(key, kwlist[i])) { match = 1; break; } @@ -1961,10 +1964,13 @@ parser_clear(struct _PyArg_Parser *parse } static PyObject* -find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key) +find_keyword(PyObject *kwargs, PyObject *kwnames, PyObject **kwstack, PyObject *key) { Py_ssize_t i, nkwargs; + if (kwargs != NULL) { + return PyDict_GetItem(kwargs, key); + } nkwargs = PyTuple_GET_SIZE(kwnames); for (i=0; i < nkwargs; i++) { PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); @@ -1976,7 +1982,7 @@ find_keyword(PyObject *kwnames, PyObject } if (!PyUnicode_Check(kwname)) { /* ignore non-string keyword keys: - an error will be raised above */ + an error will be raised below */ continue; } if (_PyUnicode_EQ(kwname, key)) { @@ -1988,7 +1994,7 @@ find_keyword(PyObject *kwnames, PyObject static int vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, - PyObject *keywords, PyObject *kwnames, + PyObject *kwargs, PyObject *kwnames, struct _PyArg_Parser *parser, va_list *p_va, int flags) { @@ -1999,7 +2005,7 @@ vgetargskeywordsfast_impl(PyObject **arg const char *msg; PyObject *keyword; int i, pos, len; - Py_ssize_t nkeywords; + Py_ssize_t nkwargs; PyObject *current_arg; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelist_t freelist; @@ -2009,10 +2015,9 @@ vgetargskeywordsfast_impl(PyObject **arg freelist.first_available = 0; freelist.entries_malloced = 0; - assert(keywords == NULL || PyDict_Check(keywords)); + assert(kwargs == NULL || PyDict_Check(kwargs)); assert(kwnames == NULL || PyTuple_Check(kwnames)); - assert((keywords != NULL || kwnames != NULL) - || (keywords == NULL && kwnames == NULL)); + assert(kwargs == NULL || kwnames == NULL); assert(parser != NULL); assert(p_va != NULL); @@ -2033,24 +2038,24 @@ vgetargskeywordsfast_impl(PyObject **arg freelist.entries_malloced = 1; } - if (keywords != NULL) { - nkeywords = PyDict_GET_SIZE(keywords); + if (kwargs != NULL) { + nkwargs = PyDict_GET_SIZE(kwargs); } else if (kwnames != NULL) { - nkeywords = PyTuple_GET_SIZE(kwnames); + nkwargs = PyTuple_GET_SIZE(kwnames); kwstack = args + nargs; } else { - nkeywords = 0; + nkwargs = 0; } - if (nargs + nkeywords > len) { + if (nargs + nkwargs > len) { PyErr_Format(PyExc_TypeError, "%s%s takes at most %d argument%s (%zd given)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", len, (len == 1) ? "" : "s", - nargs + nkeywords); + nargs + nkwargs); return cleanreturn(0, &freelist); } if (parser->max < nargs) { @@ -2064,7 +2069,6 @@ vgetargskeywordsfast_impl(PyObject **arg format = parser->format; /* convert tuple args and keyword args in same loop, using kwtuple to drive process */ for (i = 0; i < len; i++) { - keyword = (i >= pos) ? PyTuple_GET_ITEM(kwtuple, i - pos) : NULL; if (*format == '|') { format++; } @@ -2073,31 +2077,17 @@ vgetargskeywordsfast_impl(PyObject **arg } assert(!IS_END_OF_FORMAT(*format)); - current_arg = NULL; - if (nkeywords && i >= pos) { - if (keywords != NULL) { - current_arg = PyDict_GetItem(keywords, keyword); - if (!current_arg && PyErr_Occurred()) { - return cleanreturn(0, &freelist); - } - } - else { - current_arg = find_keyword(kwnames, kwstack, keyword); - } + if (i < nargs) { + current_arg = args[i]; } - if (current_arg) { - --nkeywords; - if (i < nargs) { - /* arg present in tuple and in dict */ - PyErr_Format(PyExc_TypeError, - "Argument given by name ('%U') " - "and position (%d)", - keyword, i+1); - return cleanreturn(0, &freelist); - } + else if (nkwargs && i >= pos) { + keyword = PyTuple_GET_ITEM(kwtuple, i - pos); + current_arg = find_keyword(kwargs, kwnames, kwstack, keyword); + if (current_arg) + --nkwargs; } - else if (i < nargs) { - current_arg = args[i]; + else { + current_arg = NULL; } if (current_arg) { @@ -2113,13 +2103,15 @@ vgetargskeywordsfast_impl(PyObject **arg if (i < parser->min) { /* Less arguments than required */ if (i < pos) { + Py_ssize_t min = Py_MIN(pos, parser->min); PyErr_Format(PyExc_TypeError, "Function takes %s %d positional arguments" " (%d given)", - (Py_MIN(pos, parser->min) < parser->max) ? "at least" : "exactly", - Py_MIN(pos, parser->min), nargs); + min < parser->max ? "at least" : "exactly", + min, nargs); } else { + keyword = PyTuple_GET_ITEM(kwtuple, i - pos); PyErr_Format(PyExc_TypeError, "Required argument " "'%U' (pos %d) not found", keyword, i+1); @@ -2130,7 +2122,7 @@ vgetargskeywordsfast_impl(PyObject **arg * fulfilled and no keyword args left, with no further * validation. XXX Maybe skip this in debug build ? */ - if (!nkeywords) { + if (!nkwargs) { return cleanreturn(1, &freelist); } @@ -2142,54 +2134,50 @@ vgetargskeywordsfast_impl(PyObject **arg assert(IS_END_OF_FORMAT(*format) || (*format == '|') || (*format == '$')); - /* make sure there are no extraneous keyword arguments */ - if (nkeywords > 0) { - if (keywords != NULL) { - PyObject *key, *value; - Py_ssize_t pos = 0; - while (PyDict_Next(keywords, &pos, &key, &value)) { - int match; - if (!PyUnicode_Check(key)) { - PyErr_SetString(PyExc_TypeError, - "keywords must be strings"); - return cleanreturn(0, &freelist); - } - match = PySequence_Contains(kwtuple, key); - if (match <= 0) { - if (!match) { - PyErr_Format(PyExc_TypeError, - "'%U' is an invalid keyword " - "argument for this function", - key); - } - return cleanreturn(0, &freelist); - } + if (nkwargs > 0) { + Py_ssize_t j; + /* make sure there are no arguments given by name and position */ + for (i = pos; i < nargs; i++) { + keyword = PyTuple_GET_ITEM(kwtuple, i - pos); + current_arg = find_keyword(kwargs, kwnames, kwstack, keyword); + if (current_arg) { + /* arg present in tuple and in dict */ + PyErr_Format(PyExc_TypeError, + "Argument given by name ('%U') " + "and position (%d)", + keyword, i+1); + return cleanreturn(0, &freelist); } } - else { - Py_ssize_t j, nkwargs; + /* make sure there are no extraneous keyword arguments */ + j = 0; + while (1) { + int match; + if (kwargs != NULL) { + if (!PyDict_Next(kwargs, &j, &keyword, NULL)) + break; + } + else { + if (j >= PyTuple_GET_SIZE(kwnames)) + break; + keyword = PyTuple_GET_ITEM(kwnames, j); + j++; + } - nkwargs = PyTuple_GET_SIZE(kwnames); - for (j=0; j < nkwargs; j++) { - PyObject *key = PyTuple_GET_ITEM(kwnames, j); - int match; - - if (!PyUnicode_Check(key)) { - PyErr_SetString(PyExc_TypeError, - "keywords must be strings"); - return cleanreturn(0, &freelist); + if (!PyUnicode_Check(keyword)) { + PyErr_SetString(PyExc_TypeError, + "keywords must be strings"); + return cleanreturn(0, &freelist); + } + match = PySequence_Contains(kwtuple, keyword); + if (match <= 0) { + if (!match) { + PyErr_Format(PyExc_TypeError, + "'%U' is an invalid keyword " + "argument for this function", + keyword); } - - match = PySequence_Contains(kwtuple, key); - if (match <= 0) { - if (!match) { - PyErr_Format(PyExc_TypeError, - "'%U' is an invalid keyword " - "argument for this function", - key); - } - return cleanreturn(0, &freelist); - } + return cleanreturn(0, &freelist); } } }