diff -r 1756beed417c Python/getargs.c --- a/Python/getargs.c Sat Dec 17 09:19:11 2016 +0100 +++ b/Python/getargs.c Sat Dec 17 19:23:29 2016 +0200 @@ -1960,30 +1960,83 @@ parser_clear(struct _PyArg_Parser *parse Py_CLEAR(parser->kwtuple); } -static PyObject* -find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key) +static int +next_keyword(PyObject *keywords, PyObject *kwnames, PyObject **kwstack, + Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) { - Py_ssize_t i, nkwargs; + if (keywords) { + return PyDict_Next(keywords, ppos, pkey, pvalue); + } + else { + Py_ssize_t i = *ppos; + if (i >= PyTuple_GET_SIZE(kwnames)) + return 0; + *ppos = i + 1; + *pkey = PyTuple_GET_ITEM(kwnames, i); + *pvalue = kwstack[i]; + return 1; + } +} - nkwargs = PyTuple_GET_SIZE(kwnames); - for (i=0; i < nkwargs; i++) { - PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); - - /* ptr==ptr should match in most cases since keyword keys - should be interned strings */ - if (kwname == key) { - return kwstack[i]; +static int +unpack_keywords(PyObject *keywords, PyObject *kwnames, PyObject **kwstack, + PyObject *kwtuple, + int len, int nargs, PyObject **kwargs) +{ + /* unpack keyword args using kwtuple to drive process */ + PyObject *key, *value; + Py_ssize_t pos = 0; + int i, maxarg = nargs, kwsize = PyTuple_GET_SIZE(kwtuple); + PyObject **kwitems = &PyTuple_GET_ITEM(kwtuple, 0); + for (i = nargs; i < len; i++) { + kwargs[i - nargs] = NULL; + } + while (next_keyword(keywords, kwnames, kwstack, &pos, &key, &value)) { + if (!PyUnicode_Check(key)) { + PyErr_SetString(PyExc_TypeError, + "keywords must be strings"); + return -1; } - if (!PyUnicode_Check(kwname)) { - /* ignore non-string keyword keys: - an error will be raised above */ - continue; + for (i = 0; i < kwsize; i++) { + /* ptr==ptr should match in most cases since keyword keys + should be interned strings */ + if (kwitems[i] == key) + break; } - if (_PyUnicode_EQ(kwname, key)) { - return kwstack[i]; + if (i >= kwsize) { + for (i = 0; i < kwsize; i++) { + if (_PyUnicode_EQ(kwitems[i], key)) + break; + } + if (i >= kwsize) { + PyErr_Format(PyExc_TypeError, + "'%U' is an invalid keyword " + "argument for this function", + key); + return -1; + } } + i += len - kwsize; + if (i < nargs) { + /* arg present in tuple and in dict */ + PyErr_Format(PyExc_TypeError, + "Argument given by name ('%U') " + "and position (%d)", + key, i+1); + return -1; + } + if (kwargs[i - nargs] != NULL) { + PyErr_Format(PyExc_TypeError, + "Argument given by name ('%U') " + "specified twice", + key); + return -1; + } + kwargs[i - nargs] = value; + if (i >= maxarg) + maxarg = i + 1; } - return NULL; + return maxarg; } static int @@ -1992,18 +2045,18 @@ vgetargskeywordsfast_impl(PyObject **arg struct _PyArg_Parser *parser, va_list *p_va, int flags) { - PyObject *kwtuple; char msgbuf[512]; int levels[32]; const char *format; const char *msg; - PyObject *keyword; - int i, pos, len; + int i, len, maxarg; Py_ssize_t nkeywords; PyObject *current_arg; freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; freelist_t freelist; PyObject **kwstack = NULL; + PyObject *static_kwargs[STATIC_FREELIST_ENTRIES]; + PyObject **kwargs = static_kwargs; freelist.entries = static_entries; freelist.first_available = 0; @@ -2020,10 +2073,7 @@ vgetargskeywordsfast_impl(PyObject **arg return 0; } - kwtuple = parser->kwtuple; - pos = parser->pos; - len = pos + PyTuple_GET_SIZE(kwtuple); - + len = parser->pos + PyTuple_GET_SIZE(parser->kwtuple); if (len > STATIC_FREELIST_ENTRIES) { freelist.entries = PyMem_NEW(freelistentry_t, len); if (freelist.entries == NULL) { @@ -2032,6 +2082,7 @@ vgetargskeywordsfast_impl(PyObject **arg } freelist.entries_malloced = 1; } + memset(freelist.entries, 0, sizeof(freelistentry_t) * len); if (keywords != NULL) { nkeywords = PyDict_GET_SIZE(keywords); @@ -2051,150 +2102,96 @@ vgetargskeywordsfast_impl(PyObject **arg len, (len == 1) ? "" : "s", nargs + nkeywords); - return cleanreturn(0, &freelist); + goto error; } if (parser->max < nargs) { PyErr_Format(PyExc_TypeError, "Function takes %s %d positional arguments (%d given)", (parser->min != INT_MAX) ? "at most" : "exactly", parser->max, nargs); - return cleanreturn(0, &freelist); + goto error; } + maxarg = nargs; + if (nkeywords > 0) { + if (len - nargs > STATIC_FREELIST_ENTRIES) { + kwargs = PyMem_NEW(PyObject *, len - nargs); + if (kwargs == NULL) { + PyErr_NoMemory(); + goto error; + } + } + memset(kwargs, 0, sizeof(PyObject *) * (len - nargs)); + maxarg = unpack_keywords(keywords, kwnames, kwstack, + parser->kwtuple, len, + nargs, kwargs); + if (maxarg < 0) { + goto error; + } + } 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; + for (i = 0; i < maxarg; i++) { if (*format == '|') { format++; } if (*format == '$') { format++; } - assert(!IS_END_OF_FORMAT(*format)); + assert(!IS_END_OF_FORMAT(*format) && (*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 (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 (i < nargs) { + if (i < nargs) { + /* positional argument */ current_arg = args[i]; } - - if (current_arg) { - msg = convertitem(current_arg, &format, p_va, flags, - levels, msgbuf, sizeof(msgbuf), &freelist); - if (msg) { - seterror(i+1, msg, levels, parser->fname, parser->custom_msg); - return cleanreturn(0, &freelist); - } - continue; - } - - if (i < parser->min) { - /* Less arguments than required */ - if (i < pos) { - 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); - } - else { - PyErr_Format(PyExc_TypeError, "Required argument " - "'%U' (pos %d) not found", - keyword, i+1); - } - return cleanreturn(0, &freelist); - } - /* current code reports success when all required args - * fulfilled and no keyword args left, with no further - * validation. XXX Maybe skip this in debug build ? - */ - if (!nkeywords) { - return cleanreturn(1, &freelist); - } - - /* We are into optional args, skip thru to any remaining - * keyword args */ - msg = skipitem(&format, p_va, flags); - assert(msg == NULL); - } - - 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); + else { + /* keyword argument */ + current_arg = kwargs[i - nargs]; + if (!current_arg) { + if (i < parser->min) { + /* Less arguments than required */ + if (i < parser->pos) { + i = Py_MIN(parser->pos, parser->min); + PyErr_Format(PyExc_TypeError, + "Function takes %s %d positional arguments" + " (%d given)", + i < parser->max ? "at least" : "exactly", + i, nargs); + } + else { + PyErr_Format(PyExc_TypeError, "Required argument " + "'%s' (pos %d) not found", + parser->keywords[i], i+1); + } + goto error; } - 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); + else { + /* We are into optional args, skip thru to any remaining + * keyword args */ + msg = skipitem(&format, p_va, flags); + assert(msg == NULL); + continue; } } } - else { - Py_ssize_t j, nkwargs; - 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); - } - - 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); - } - } + msg = convertitem(current_arg, &format, p_va, flags, + levels, msgbuf, sizeof(msgbuf), &freelist); + if (msg) { + seterror(i+1, msg, levels, parser->fname, parser->custom_msg); + goto error; } } + if (kwargs != static_kwargs) { + PyMem_FREE(kwargs); + } return cleanreturn(1, &freelist); + + error: + if (kwargs != static_kwargs) { + PyMem_FREE(kwargs); + } + return cleanreturn(0, &freelist); } static int