--- getargs_svn.c 2007-03-07 21:52:11.000000000 -0500 +++ getargs.c 2007-04-04 01:09:43.000000000 -0400 @@ -1343,194 +1343,126 @@ return retval; } +#define IS_END_OF_FORMAT(c) (c=='\0' || c==';' || c==':') static int -vgetargskeywords(PyObject *args, PyObject *keywords, const char *format, - char **kwlist, va_list *p_va, int flags) +vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, + char **keywords, va_list *p_va, int flags) { char msgbuf[512]; int levels[32]; - const char *fname, *message; - int min, max; - const char *formatsave; - int i, len, nargs, nkeywords; - const char *msg; - char **p; - PyObject *freelist = NULL; + char *fname, *msg, *custom_msg, *keyword; + int min=INT_MAX; + int i, nkeywords, ntupleargs, nkeywordargs; + PyObject *freelist = NULL, *current_arg; 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(keywords != NULL); assert(p_va != NULL); - /* Search the format: - message <- error msg, if any (else NULL). - fname <- routine name, if any (else NULL). - min <- # of required arguments, or -1 if all are required. - max <- most arguments (required + optional). - Check that kwlist has a non-NULL entry for each arg. - Raise error if a tuple arg spec is found. - */ - fname = message = NULL; - formatsave = format; - p = kwlist; - min = -1; - max = 0; - while ((i = *format++) != '\0') { - if (isalpha(Py_CHARMASK(i)) && i != 'e') { - max++; - if (*p == NULL) { - PyErr_SetString(PyExc_RuntimeError, - "more argument specifiers than " - "keyword list entries"); - return 0; - } - p++; - } - else if (i == '|') - min = max; - else if (i == ':') { - fname = format; - break; - } - else if (i == ';') { - message = format; - break; - } - else if (i == '(') { - PyErr_SetString(PyExc_RuntimeError, - "tuple found in format when using keyword " - "arguments"); - return 0; - } - } - format = formatsave; - if (*p != NULL) { - PyErr_SetString(PyExc_RuntimeError, - "more keyword list entries than " - "argument specifiers"); + /* grab the function name or custom error msg first (mutually exclusive) */ + fname=strchr(format, ':'); + if (fname){ + fname++; + custom_msg=NULL; + } + else{ + custom_msg=strchr(format,';'); + if (custom_msg) + custom_msg++; + } + + /* scan keywords and get greatest possible nbr of args */ + for (nkeywords=0; keywords[nkeywords]; nkeywords++) + continue; + ntupleargs = PyTuple_GET_SIZE(args); + nkeywordargs = kwargs == NULL ? 0 : PyDict_Size(kwargs); + if (ntupleargs+nkeywordargs > nkeywords){ + PyErr_Format(PyExc_TypeError, "%s%s takes at most %d argument%s " + "(%d given)", + fname==NULL ? "function" : fname, + fname==NULL ? "" : "()", + nkeywords, nkeywords==1 ? "" : "s", + ntupleargs+nkeywordargs); return 0; } - if (min < 0) { - /* All arguments are required. */ - min = max; - } - - nargs = PyTuple_GET_SIZE(args); - nkeywords = keywords == NULL ? 0 : PyDict_Size(keywords); - - /* make sure there are no duplicate values for an argument; - its not clear when to use the term "keyword argument vs. - keyword parameter in messages */ - if (nkeywords > 0) { - for (i = 0; i < nargs; i++) { - const char *thiskw = kwlist[i]; - if (thiskw == NULL) - break; - if (PyDict_GetItemString(keywords, thiskw)) { - PyErr_Format(PyExc_TypeError, - "keyword parameter '%s' was given " - "by position and by name", - thiskw); - return 0; - } - else if (PyErr_Occurred()) - return 0; - } - } - /* required arguments missing from args can be supplied by keyword - arguments; set len to the number of positional arguments, and, - if that's less than the minimum required, add in the number of - required arguments that are supplied by keywords */ - len = nargs; - if (nkeywords > 0 && nargs < min) { - for (i = nargs; i < min; i++) { - if (PyDict_GetItemString(keywords, kwlist[i])) - len++; - else if (PyErr_Occurred()) - return 0; - } - } - /* make sure we got an acceptable number of arguments; the message - is a little confusing with keywords since keyword arguments - which are supplied, but don't match the required arguments - are not included in the "%d given" part of the message - XXX and this isn't a bug!? */ - if (len < min || max < len) { - if (message == NULL) { - PyOS_snprintf(msgbuf, sizeof(msgbuf), - "%.200s%s takes %s %d argument%s " - "(%d given)", - fname==NULL ? "function" : fname, - fname==NULL ? "" : "()", - min==max ? "exactly" - : len < min ? "at least" : "at most", - len < min ? min : max, - (len < min ? min : max) == 1 ? "" : "s", - len); - message = msgbuf; - } - PyErr_SetString(PyExc_TypeError, message); - return 0; - } - - /* convert the positional arguments */ - for (i = 0; i < nargs; i++) { - if (*format == '|') + /* convert tuple args and keyword args in same loop, using keywords to drive process */ + for (i = 0; i < nkeywords; i++) { + keyword=keywords[i]; + if (*format == '|'){ + min=i; format++; - msg = convertitem(PyTuple_GET_ITEM(args, i), &format, p_va, - flags, levels, msgbuf, sizeof(msgbuf), - &freelist); - if (msg) { - seterror(i+1, msg, levels, fname, message); - return cleanreturn(0, freelist); } - } - - /* handle no keyword parameters in call */ - if (nkeywords == 0) - return cleanreturn(1, freelist); - - /* convert the keyword arguments; this uses the format - string where it was left after processing args */ - for (i = nargs; i < max; i++) { - PyObject *item; - if (*format == '|') - format++; - item = PyDict_GetItemString(keywords, kwlist[i]); - if (item != NULL) { - Py_INCREF(item); - msg = convertitem(item, &format, p_va, flags, levels, - msgbuf, sizeof(msgbuf), &freelist); - Py_DECREF(item); - if (msg) { - seterror(i+1, msg, levels, fname, message); + if (IS_END_OF_FORMAT(*format)){ + PyErr_Format(PyExc_RuntimeError, + "More keyword list entries (%d) than format specifiers (%d)", nkeywords, i); + return cleanreturn(0, freelist); + } + current_arg=NULL; + if (nkeywordargs) + current_arg=PyDict_GetItemString(kwargs, keyword); + if (current_arg){ + --nkeywordargs; + if (i 0) { + if (nkeywordargs) { PyObject *key, *value; Py_ssize_t pos = 0; - while (PyDict_Next(keywords, &pos, &key, &value)) { + while (PyDict_Next(kwargs, &pos, &key, &value)) { int match = 0; char *ks; if (!PyString_Check(key)) { @@ -1539,8 +1471,8 @@ return cleanreturn(0, freelist); } ks = PyString_AsString(key); - for (i = 0; i < max; i++) { - if (!strcmp(ks, kwlist[i])) { + for (i = 0; i < nkeywords; i++) { + if (!strcmp(ks, keywords[i])) { match = 1; break; } @@ -1562,7 +1494,7 @@ static char * skipitem(const char **p_format, va_list *p_va, int flags) { - const char *format = *p_format; + const char *format = *p_format; char c = *format++; switch (c) { @@ -1669,16 +1601,32 @@ } break; } - + + case '(': /* bypass tuple, not handled at all previously */ + { + char *msg; + while (1){ + if (*format==')') + break; + if (IS_END_OF_FORMAT(*format)) + return "Unmatched left paren in format string"; + msg=skipitem(&format, p_va, flags); + if (msg) + return msg; + } + format++; + break; + } + + case ')': + return "Unmatched right paren in format string"; + default: err: return "impossible"; } - /* The "(...)" format code for tuples is not handled here because - * it is not allowed with keyword args. */ - *p_format = format; return NULL; }