diff options
Diffstat (limited to 'Python/getargs.c')
| -rw-r--r-- | Python/getargs.c | 867 | 
1 files changed, 731 insertions, 136 deletions
| diff --git a/Python/getargs.c b/Python/getargs.c index b10e776..616c6eb 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -18,16 +18,28 @@ int PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,  int PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *,                                  const char *, char **, va_list); +int _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *, +                                            struct _PyArg_Parser *, ...); +int _PyArg_VaParseTupleAndKeywordsFast(PyObject *, PyObject *, +                                            struct _PyArg_Parser *, va_list); +  #ifdef HAVE_DECLSPEC_DLL  /* Export functions */ -PyAPI_FUNC(int) _PyArg_Parse_SizeT(PyObject *, char *, ...); -PyAPI_FUNC(int) _PyArg_ParseTuple_SizeT(PyObject *, char *, ...); +PyAPI_FUNC(int) _PyArg_Parse_SizeT(PyObject *, const char *, ...); +PyAPI_FUNC(int) _PyArg_ParseStack_SizeT(PyObject **args, Py_ssize_t nargs, PyObject *kwnames, +                                        struct _PyArg_Parser *parser, ...); +PyAPI_FUNC(int) _PyArg_ParseTuple_SizeT(PyObject *, const char *, ...);  PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywords_SizeT(PyObject *, PyObject *,                                                    const char *, char **, ...);  PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...); -PyAPI_FUNC(int) _PyArg_VaParse_SizeT(PyObject *, char *, va_list); +PyAPI_FUNC(int) _PyArg_VaParse_SizeT(PyObject *, const char *, va_list);  PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *, PyObject *,                                                const char *, char **, va_list); + +PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast_SizeT(PyObject *, PyObject *, +                                            struct _PyArg_Parser *, ...); +PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywordsFast_SizeT(PyObject *, PyObject *, +                                            struct _PyArg_Parser *, va_list);  #endif  #define FLAG_COMPAT 1 @@ -56,18 +68,24 @@ typedef struct {  /* Forward */  static int vgetargs1(PyObject *, const char *, va_list *, int);  static void seterror(Py_ssize_t, const char *, int *, const char *, const char *); -static char *convertitem(PyObject *, const char **, va_list *, int, int *, -                         char *, size_t, freelist_t *); -static char *converttuple(PyObject *, const char **, va_list *, int, -                          int *, char *, size_t, int, freelist_t *); -static char *convertsimple(PyObject *, const char **, va_list *, int, char *, -                           size_t, freelist_t *); -static Py_ssize_t convertbuffer(PyObject *, void **p, char **); -static int getbuffer(PyObject *, Py_buffer *, char**); +static const char *convertitem(PyObject *, const char **, va_list *, int, int *, +                               char *, size_t, freelist_t *); +static const char *converttuple(PyObject *, const char **, va_list *, int, +                                int *, char *, size_t, int, freelist_t *); +static const char *convertsimple(PyObject *, const char **, va_list *, int, +                                 char *, size_t, freelist_t *); +static Py_ssize_t convertbuffer(PyObject *, void **p, const char **); +static int getbuffer(PyObject *, Py_buffer *, const char**);  static int vgetargskeywords(PyObject *, PyObject *,                              const char *, char **, va_list *, int); -static char *skipitem(const char **, va_list *, int); +static int vgetargskeywordsfast(PyObject *, PyObject *, +                            struct _PyArg_Parser *, va_list *, int); +static int vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, +                          PyObject *keywords, PyObject *kwnames, +                          struct _PyArg_Parser *parser, +                          va_list *p_va, int flags); +static const char *skipitem(const char **, va_list *, int);  int  PyArg_Parse(PyObject *args, const char *format, ...) @@ -82,7 +100,7 @@ PyArg_Parse(PyObject *args, const char *format, ...)  }  int -_PyArg_Parse_SizeT(PyObject *args, char *format, ...) +_PyArg_Parse_SizeT(PyObject *args, const char *format, ...)  {      int retval;      va_list va; @@ -107,7 +125,7 @@ PyArg_ParseTuple(PyObject *args, const char *format, ...)  }  int -_PyArg_ParseTuple_SizeT(PyObject *args, char *format, ...) +_PyArg_ParseTuple_SizeT(PyObject *args, const char *format, ...)  {      int retval;      va_list va; @@ -123,20 +141,26 @@ int  PyArg_VaParse(PyObject *args, const char *format, va_list va)  {      va_list lva; +    int retval; -        Py_VA_COPY(lva, va); +    va_copy(lva, va); -    return vgetargs1(args, format, &lva, 0); +    retval = vgetargs1(args, format, &lva, 0); +    va_end(lva); +    return retval;  }  int -_PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va) +_PyArg_VaParse_SizeT(PyObject *args, const char *format, va_list va)  {      va_list lva; +    int retval; -        Py_VA_COPY(lva, va); +    va_copy(lva, va); -    return vgetargs1(args, format, &lva, FLAG_SIZE_T); +    retval = vgetargs1(args, format, &lva, FLAG_SIZE_T); +    va_end(lva); +    return retval;  } @@ -208,7 +232,7 @@ vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags)      int endfmt = 0;      const char *formatsave = format;      Py_ssize_t i, len; -    char *msg; +    const char *msg;      int compat = flags & FLAG_COMPAT;      freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];      freelist_t freelist; @@ -394,7 +418,12 @@ seterror(Py_ssize_t iarg, const char *msg, int *levels, const char *fname,          PyOS_snprintf(p, sizeof(buf) - (p - buf), " %.256s", msg);          message = buf;      } -    PyErr_SetString(PyExc_TypeError, message); +    if (msg[0] == '(') { +        PyErr_SetString(PyExc_SystemError, message); +    } +    else { +        PyErr_SetString(PyExc_TypeError, message); +    }  } @@ -416,7 +445,7 @@ seterror(Py_ssize_t iarg, const char *msg, int *levels, const char *fname,        and msgbuf is returned.  */ -static char * +static const char *  converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,               int *levels, char *msgbuf, size_t bufsize, int toplevel,               freelist_t *freelist) @@ -474,7 +503,7 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,      format = *p_format;      for (i = 0; i < n; i++) { -        char *msg; +        const char *msg;          PyObject *item;          item = PySequence_GetItem(arg, i);          if (item == NULL) { @@ -501,11 +530,11 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,  /* Convert a single item. */ -static char * +static const char *  convertitem(PyObject *arg, const char **p_format, va_list *p_va, int flags,              int *levels, char *msgbuf, size_t bufsize, freelist_t *freelist)  { -    char *msg; +    const char *msg;      const char *format = *p_format;      if (*format == '(' /* ')' */) { @@ -530,7 +559,7 @@ convertitem(PyObject *arg, const char **p_format, va_list *p_va, int flags,  /* Format an error message generated by convertsimple(). */ -static char * +static const char *  converterr(const char *expected, PyObject *arg, char *msgbuf, size_t bufsize)  {      assert(expected != NULL); @@ -572,7 +601,7 @@ float_argument_error(PyObject *arg)     When you add new format codes, please don't forget poor skipitem() below.  */ -static char * +static const char *  convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,                char *msgbuf, size_t bufsize, freelist_t *freelist)  { @@ -752,14 +781,13 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,          break;      } -#ifdef HAVE_LONG_LONG -    case 'L': {/* PY_LONG_LONG */ -        PY_LONG_LONG *p = va_arg( *p_va, PY_LONG_LONG * ); -        PY_LONG_LONG ival; +    case 'L': {/* long long */ +        long long *p = va_arg( *p_va, long long * ); +        long long ival;          if (float_argument_error(arg))              RETURN_ERR_OCCURRED;          ival = PyLong_AsLongLong(arg); -        if (ival == (PY_LONG_LONG)-1 && PyErr_Occurred()) +        if (ival == (long long)-1 && PyErr_Occurred())              RETURN_ERR_OCCURRED;          else              *p = ival; @@ -767,8 +795,8 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,      }      case 'K': { /* long long sized bitfield */ -        unsigned PY_LONG_LONG *p = va_arg(*p_va, unsigned PY_LONG_LONG *); -        unsigned PY_LONG_LONG ival; +        unsigned long long *p = va_arg(*p_va, unsigned long long *); +        unsigned long long ival;          if (PyLong_Check(arg))              ival = PyLong_AsUnsignedLongLongMask(arg);          else @@ -776,7 +804,6 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,          *p = ival;          break;      } -#endif      case 'f': {/* float */          float *p = va_arg(*p_va, float *); @@ -857,7 +884,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,      case 'y': {/* any bytes-like object */          void **p = (void **)va_arg(*p_va, char **); -        char *buf; +        const char *buf;          Py_ssize_t count;          if (*format == '*') {              if (getbuffer(arg, (Py_buffer*)p, &buf) < 0) @@ -904,7 +931,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,                  PyBuffer_FillInfo(p, arg, sarg, len, 1, 0);              }              else { /* any bytes-like object */ -                char *buf; +                const char *buf;                  if (getbuffer(arg, p, &buf) < 0)                      return converterr(buf, arg, msgbuf, bufsize);              } @@ -934,7 +961,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,              }              else { /* read-only bytes-like object */                  /* XXX Really? */ -                char *buf; +                const char *buf;                  Py_ssize_t count = convertbuffer(arg, p, &buf);                  if (count < 0)                      return converterr(buf, arg, msgbuf, bufsize); @@ -1051,35 +1078,25 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,                  return converterr("(AsCharBuffer failed)",                                    arg, msgbuf, bufsize);          } -        else { -            PyObject *u; - -            /* Convert object to Unicode */ -            u = PyUnicode_FromObject(arg); -            if (u == NULL) -                return converterr( -                    "string or unicode or text buffer", -                    arg, msgbuf, bufsize); - +        else if (PyUnicode_Check(arg)) {              /* Encode object; use default error handling */ -            s = PyUnicode_AsEncodedString(u, +            s = PyUnicode_AsEncodedString(arg,                                            encoding,                                            NULL); -            Py_DECREF(u);              if (s == NULL)                  return converterr("(encoding failed)",                                    arg, msgbuf, bufsize); -            if (!PyBytes_Check(s)) { -                Py_DECREF(s); -                return converterr( -                    "(encoder failed to return bytes)", -                    arg, msgbuf, bufsize); -            } +            assert(PyBytes_Check(s));              size = PyBytes_GET_SIZE(s);              ptr = PyBytes_AS_STRING(s);              if (ptr == NULL)                  ptr = "";          } +        else { +            return converterr( +                recode_strings ? "str" : "str, bytes or bytearray", +                arg, msgbuf, bufsize); +        }          /* Write output; output is guaranteed to be 0-terminated */          if (*format == '#') { @@ -1129,7 +1146,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,              } else {                  if (size + 1 > BUFFER_LEN) {                      Py_DECREF(s); -                    PyErr_Format(PyExc_TypeError, +                    PyErr_Format(PyExc_ValueError,                                   "encoded string too long "                                   "(%zd, maximum length %zd)",                                   (Py_ssize_t)size, (Py_ssize_t)(BUFFER_LEN-1)); @@ -1283,7 +1300,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,  }  static Py_ssize_t -convertbuffer(PyObject *arg, void **p, char **errmsg) +convertbuffer(PyObject *arg, void **p, const char **errmsg)  {      PyBufferProcs *pb = Py_TYPE(arg)->tp_as_buffer;      Py_ssize_t count; @@ -1305,7 +1322,7 @@ convertbuffer(PyObject *arg, void **p, char **errmsg)  }  static int -getbuffer(PyObject *arg, Py_buffer *view, char **errmsg) +getbuffer(PyObject *arg, Py_buffer *view, const char **errmsg)  {      if (PyObject_GetBuffer(arg, view, PyBUF_SIMPLE) != 0) {          *errmsg = "bytes-like object"; @@ -1391,9 +1408,10 @@ PyArg_VaParseTupleAndKeywords(PyObject *args,          return 0;      } -        Py_VA_COPY(lva, va); +    va_copy(lva, va);      retval = vgetargskeywords(args, keywords, format, kwlist, &lva, 0); +    va_end(lva);      return retval;  } @@ -1415,10 +1433,138 @@ _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *args,          return 0;      } -        Py_VA_COPY(lva, va); +    va_copy(lva, va);      retval = vgetargskeywords(args, keywords, format,                                kwlist, &lva, FLAG_SIZE_T); +    va_end(lva); +    return retval; +} + +int +_PyArg_ParseTupleAndKeywordsFast(PyObject *args, PyObject *keywords, +                            struct _PyArg_Parser *parser, ...) +{ +    int retval; +    va_list va; + +    if ((args == NULL || !PyTuple_Check(args)) || +        (keywords != NULL && !PyDict_Check(keywords)) || +        parser == NULL) +    { +        PyErr_BadInternalCall(); +        return 0; +    } + +    va_start(va, parser); +    retval = vgetargskeywordsfast(args, keywords, parser, &va, 0); +    va_end(va); +    return retval; +} + +int +_PyArg_ParseTupleAndKeywordsFast_SizeT(PyObject *args, PyObject *keywords, +                            struct _PyArg_Parser *parser, ...) +{ +    int retval; +    va_list va; + +    if ((args == NULL || !PyTuple_Check(args)) || +        (keywords != NULL && !PyDict_Check(keywords)) || +        parser == NULL) +    { +        PyErr_BadInternalCall(); +        return 0; +    } + +    va_start(va, parser); +    retval = vgetargskeywordsfast(args, keywords, parser, &va, FLAG_SIZE_T); +    va_end(va); +    return retval; +} + +int +_PyArg_ParseStack(PyObject **args, Py_ssize_t nargs, PyObject *kwnames, +                  struct _PyArg_Parser *parser, ...) +{ +    int retval; +    va_list va; + +    if ((kwnames != NULL && !PyTuple_Check(kwnames)) || +        parser == NULL) +    { +        PyErr_BadInternalCall(); +        return 0; +    } + +    va_start(va, parser); +    retval = vgetargskeywordsfast_impl(args, nargs, NULL, kwnames, parser, &va, 0); +    va_end(va); +    return retval; +} + +int +_PyArg_ParseStack_SizeT(PyObject **args, Py_ssize_t nargs, PyObject *kwnames, +                        struct _PyArg_Parser *parser, ...) +{ +    int retval; +    va_list va; + +    if ((kwnames != NULL && !PyTuple_Check(kwnames)) || +        parser == NULL) +    { +        PyErr_BadInternalCall(); +        return 0; +    } + +    va_start(va, parser); +    retval = vgetargskeywordsfast_impl(args, nargs, NULL, kwnames, parser, &va, FLAG_SIZE_T); +    va_end(va); +    return retval; +} + + +int +_PyArg_VaParseTupleAndKeywordsFast(PyObject *args, PyObject *keywords, +                            struct _PyArg_Parser *parser, va_list va) +{ +    int retval; +    va_list lva; + +    if ((args == NULL || !PyTuple_Check(args)) || +        (keywords != NULL && !PyDict_Check(keywords)) || +        parser == NULL) +    { +        PyErr_BadInternalCall(); +        return 0; +    } + +    va_copy(lva, va); + +    retval = vgetargskeywordsfast(args, keywords, parser, &lva, 0); +    va_end(lva); +    return retval; +} + +int +_PyArg_VaParseTupleAndKeywordsFast_SizeT(PyObject *args, PyObject *keywords, +                            struct _PyArg_Parser *parser, va_list va) +{ +    int retval; +    va_list lva; + +    if ((args == NULL || !PyTuple_Check(args)) || +        (keywords != NULL && !PyDict_Check(keywords)) || +        parser == NULL) +    { +        PyErr_BadInternalCall(); +        return 0; +    } + +    va_copy(lva, va); + +    retval = vgetargskeywordsfast(args, keywords, parser, &lva, FLAG_SIZE_T); +    va_end(lva);      return retval;  } @@ -1448,7 +1594,8 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,      const char *fname, *msg, *custom_msg, *keyword;      int min = INT_MAX;      int max = INT_MAX; -    int i, len; +    int i, pos, len; +    int skip = 0;      Py_ssize_t nargs, nkeywords;      PyObject *current_arg;      freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; @@ -1476,9 +1623,17 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,              custom_msg++;      } +    /* scan kwlist and count the number of positional-only parameters */ +    for (pos = 0; kwlist[pos] && !*kwlist[pos]; pos++) { +    }      /* scan kwlist and get greatest possible nbr of args */ -    for (len=0; kwlist[len]; len++) -        continue; +    for (len = pos; kwlist[len]; len++) { +        if (!*kwlist[len]) { +            PyErr_SetString(PyExc_SystemError, +                            "Empty keyword parameter name"); +            return cleanreturn(0, &freelist); +        } +    }      if (len > STATIC_FREELIST_ENTRIES) {          freelist.entries = PyMem_NEW(freelistentry_t, len); @@ -1507,7 +1662,7 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,          keyword = kwlist[i];          if (*format == '|') {              if (min != INT_MAX) { -                PyErr_SetString(PyExc_RuntimeError, +                PyErr_SetString(PyExc_SystemError,                                  "Invalid format string (| specified twice)");                  return cleanreturn(0, &freelist);              } @@ -1516,14 +1671,14 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,              format++;              if (max != INT_MAX) { -                PyErr_SetString(PyExc_RuntimeError, +                PyErr_SetString(PyExc_SystemError,                                  "Invalid format string ($ before |)");                  return cleanreturn(0, &freelist);              }          }          if (*format == '$') {              if (max != INT_MAX) { -                PyErr_SetString(PyExc_RuntimeError, +                PyErr_SetString(PyExc_SystemError,                                  "Invalid format string ($ specified twice)");                  return cleanreturn(0, &freelist);              } @@ -1531,6 +1686,17 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,              max = i;              format++; +            if (max < pos) { +                PyErr_SetString(PyExc_SystemError, +                                "Empty parameter name after $"); +                return cleanreturn(0, &freelist); +            } +            if (skip) { +                /* Now we know the minimal and the maximal numbers of +                 * positional arguments and can raise an exception with +                 * informative message (see below). */ +                break; +            }              if (max < nargs) {                  PyErr_Format(PyExc_TypeError,                               "Function takes %s %d positional arguments" @@ -1541,66 +1707,90 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,              }          }          if (IS_END_OF_FORMAT(*format)) { -            PyErr_Format(PyExc_RuntimeError, +            PyErr_Format(PyExc_SystemError,                           "More keyword list entries (%d) than "                           "format specifiers (%d)", len, i);              return cleanreturn(0, &freelist);          } -        current_arg = NULL; -        if (nkeywords) { -            current_arg = PyDict_GetItemString(keywords, keyword); -        } -        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); +        if (!skip) { +            current_arg = NULL; +            if (nkeywords && i >= pos) { +                current_arg = PyDict_GetItemString(keywords, keyword); +                if (!current_arg && PyErr_Occurred()) { +                    return cleanreturn(0, &freelist); +                }              } -        } -        else if (nkeywords && PyErr_Occurred()) -            return cleanreturn(0, &freelist); -        else if (i < nargs) -            current_arg = PyTuple_GET_ITEM(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, fname, custom_msg); -                return cleanreturn(0, &freelist); +            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 (i < nargs) +                current_arg = PyTuple_GET_ITEM(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, fname, custom_msg); +                    return cleanreturn(0, &freelist); +                } +                continue;              } -            continue; -        } -        if (i < min) { -            PyErr_Format(PyExc_TypeError, "Required argument " -                         "'%s' (pos %d) not found", -                         keyword, i+1); -            return cleanreturn(0, &freelist); +            if (i < min) { +                if (i < pos) { +                    assert (min == INT_MAX); +                    assert (max == INT_MAX); +                    skip = 1; +                    /* At that moment we still don't know the minimal and +                     * the maximal numbers of positional arguments.  Raising +                     * an exception is deferred until we encounter | and $ +                     * or the end of the format. */ +                } +                else { +                    PyErr_Format(PyExc_TypeError, "Required argument " +                                "'%s' (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 && !skip) { +                return cleanreturn(1, &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);          if (msg) { -            PyErr_Format(PyExc_RuntimeError, "%s: '%s'", msg, +            PyErr_Format(PyExc_SystemError, "%s: '%s'", msg,                           format);              return cleanreturn(0, &freelist);          }      } +    if (skip) { +        PyErr_Format(PyExc_TypeError, +                     "Function takes %s %d positional arguments" +                     " (%d given)", +                     (Py_MIN(pos, min) < i) ? "at least" : "exactly", +                     Py_MIN(pos, min), nargs); +        return cleanreturn(0, &freelist); +    } +      if (!IS_END_OF_FORMAT(*format) && (*format != '|') && (*format != '$')) { -        PyErr_Format(PyExc_RuntimeError, +        PyErr_Format(PyExc_SystemError,              "more argument specifiers than keyword list entries "              "(remaining format:'%s')", format);          return cleanreturn(0, &freelist); @@ -1618,7 +1808,7 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,                  return cleanreturn(0, &freelist);              }              for (i = 0; i < len; i++) { -                if (_PyUnicode_EqualToASCIIString(key, kwlist[i])) { +                if (*kwlist[i] && _PyUnicode_EqualToASCIIString(key, kwlist[i])) {                      match = 1;                      break;                  } @@ -1637,7 +1827,388 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,  } -static char * +/* List of static parsers. */ +static struct _PyArg_Parser *static_arg_parsers = NULL; + +static int +parser_init(struct _PyArg_Parser *parser) +{ +    const char * const *keywords; +    const char *format, *msg; +    int i, len, min, max, nkw; +    PyObject *kwtuple; + +    assert(parser->format != NULL); +    assert(parser->keywords != NULL); +    if (parser->kwtuple != NULL) { +        return 1; +    } + +    /* grab the function name or custom error msg first (mutually exclusive) */ +    parser->fname = strchr(parser->format, ':'); +    if (parser->fname) { +        parser->fname++; +        parser->custom_msg = NULL; +    } +    else { +        parser->custom_msg = strchr(parser->format,';'); +        if (parser->custom_msg) +            parser->custom_msg++; +    } + +    keywords = parser->keywords; +    /* scan keywords and count the number of positional-only parameters */ +    for (i = 0; keywords[i] && !*keywords[i]; i++) { +    } +    parser->pos = i; +    /* scan keywords and get greatest possible nbr of args */ +    for (; keywords[i]; i++) { +        if (!*keywords[i]) { +            PyErr_SetString(PyExc_SystemError, +                            "Empty keyword parameter name"); +            return 0; +        } +    } +    len = i; + +    min = max = INT_MAX; +    format = parser->format; +    for (i = 0; i < len; i++) { +        if (*format == '|') { +            if (min != INT_MAX) { +                PyErr_SetString(PyExc_SystemError, +                                "Invalid format string (| specified twice)"); +                return 0; +            } +            if (max != INT_MAX) { +                PyErr_SetString(PyExc_SystemError, +                                "Invalid format string ($ before |)"); +                return 0; +            } +            min = i; +            format++; +        } +        if (*format == '$') { +            if (max != INT_MAX) { +                PyErr_SetString(PyExc_SystemError, +                                "Invalid format string ($ specified twice)"); +                return 0; +            } +            if (i < parser->pos) { +                PyErr_SetString(PyExc_SystemError, +                                "Empty parameter name after $"); +                return 0; +            } +            max = i; +            format++; +        } +        if (IS_END_OF_FORMAT(*format)) { +            PyErr_Format(PyExc_SystemError, +                         "More keyword list entries (%d) than " +                         "format specifiers (%d)", len, i); +            return 0; +        } + +        msg = skipitem(&format, NULL, 0); +        if (msg) { +            PyErr_Format(PyExc_SystemError, "%s: '%s'", msg, +                         format); +            return 0; +        } +    } +    parser->min = Py_MIN(min, len); +    parser->max = Py_MIN(max, len); + +    if (!IS_END_OF_FORMAT(*format) && (*format != '|') && (*format != '$')) { +        PyErr_Format(PyExc_SystemError, +            "more argument specifiers than keyword list entries " +            "(remaining format:'%s')", format); +        return 0; +    } + +    nkw = len - parser->pos; +    kwtuple = PyTuple_New(nkw); +    if (kwtuple == NULL) { +        return 0; +    } +    keywords = parser->keywords + parser->pos; +    for (i = 0; i < nkw; i++) { +        PyObject *str = PyUnicode_FromString(keywords[i]); +        if (str == NULL) { +            Py_DECREF(kwtuple); +            return 0; +        } +        PyUnicode_InternInPlace(&str); +        PyTuple_SET_ITEM(kwtuple, i, str); +    } +    parser->kwtuple = kwtuple; + +    assert(parser->next == NULL); +    parser->next = static_arg_parsers; +    static_arg_parsers = parser; +    return 1; +} + +static void +parser_clear(struct _PyArg_Parser *parser) +{ +    Py_CLEAR(parser->kwtuple); +} + +static PyObject* +find_keyword(PyObject *kwnames, PyObject **kwstack, PyObject *key) +{ +    Py_ssize_t i, nkwargs; + +    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]; +        } +        if (!PyUnicode_Check(kwname)) { +            /* ignore non-string keyword keys: +               an error will be raised above */ +            continue; +        } +        if (_PyUnicode_EQ(kwname, key)) { +            return kwstack[i]; +        } +    } +    return NULL; +} + +static int +vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, +                          PyObject *keywords, PyObject *kwnames, +                          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; +    Py_ssize_t nkeywords; +    PyObject *current_arg; +    freelistentry_t static_entries[STATIC_FREELIST_ENTRIES]; +    freelist_t freelist; +    PyObject **kwstack = NULL; + +    freelist.entries = static_entries; +    freelist.first_available = 0; +    freelist.entries_malloced = 0; + +    assert(keywords == NULL || PyDict_Check(keywords)); +    assert(kwnames == NULL || PyTuple_Check(kwnames)); +    assert((keywords != NULL || kwnames != NULL) +           || (keywords == NULL && kwnames == NULL)); +    assert(parser != NULL); +    assert(p_va != NULL); + +    if (!parser_init(parser)) { +        return 0; +    } + +    kwtuple = parser->kwtuple; +    pos = parser->pos; +    len = pos + PyTuple_GET_SIZE(kwtuple); + +    if (len > STATIC_FREELIST_ENTRIES) { +        freelist.entries = PyMem_NEW(freelistentry_t, len); +        if (freelist.entries == NULL) { +            PyErr_NoMemory(); +            return 0; +        } +        freelist.entries_malloced = 1; +    } + +    if (keywords != NULL) { +        nkeywords = PyDict_Size(keywords); +    } +    else if (kwnames != NULL) { +        nkeywords = PyTuple_GET_SIZE(kwnames); +        kwstack = args + nargs; +    } +    else { +        nkeywords = 0; +    } +    if (nargs + nkeywords > 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); +        return cleanreturn(0, &freelist); +    } +    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); +    } + +    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++; +        } +        if (*format == '$') { +            format++; +        } +        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 (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) { +            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); +                } +                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 { +            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); +                } +            } +        } +    } + +    return cleanreturn(1, &freelist); +} + +static int +vgetargskeywordsfast(PyObject *args, PyObject *keywords, +                     struct _PyArg_Parser *parser, va_list *p_va, int flags) +{ +    PyObject **stack; +    Py_ssize_t nargs; + +    assert(args != NULL && PyTuple_Check(args)); + +    stack = &PyTuple_GET_ITEM(args, 0); +    nargs = PyTuple_GET_SIZE(args); +    return vgetargskeywordsfast_impl(stack, nargs, keywords, NULL, +                                     parser, p_va, flags); +} + + +static const char *  skipitem(const char **p_format, va_list *p_va, int flags)  {      const char *format = *p_format; @@ -1658,10 +2229,8 @@ skipitem(const char **p_format, va_list *p_va, int flags)      case 'I': /* int sized bitfield */      case 'l': /* long int */      case 'k': /* long int sized bitfield */ -#ifdef HAVE_LONG_LONG -    case 'L': /* PY_LONG_LONG */ -    case 'K': /* PY_LONG_LONG sized bitfield */ -#endif +    case 'L': /* long long */ +    case 'K': /* long long sized bitfield */      case 'n': /* Py_ssize_t */      case 'f': /* float */      case 'd': /* double */ @@ -1673,7 +2242,9 @@ skipitem(const char **p_format, va_list *p_va, int flags)      case 'Y': /* string object */      case 'U': /* unicode string object */          { -            (void) va_arg(*p_va, void *); +            if (p_va != NULL) { +                (void) va_arg(*p_va, void *); +            }              break;          } @@ -1681,7 +2252,9 @@ skipitem(const char **p_format, va_list *p_va, int flags)      case 'e': /* string with encoding */          { -            (void) va_arg(*p_va, const char *); +            if (p_va != NULL) { +                (void) va_arg(*p_va, const char *); +            }              if (!(*format == 's' || *format == 't'))                  /* after 'e', only 's' and 't' is allowed */                  goto err; @@ -1696,12 +2269,16 @@ skipitem(const char **p_format, va_list *p_va, int flags)      case 'Z': /* unicode string or None */      case 'w': /* buffer, read-write */          { -            (void) va_arg(*p_va, char **); +            if (p_va != NULL) { +                (void) va_arg(*p_va, char **); +            }              if (*format == '#') { -                if (flags & FLAG_SIZE_T) -                    (void) va_arg(*p_va, Py_ssize_t *); -                else -                    (void) va_arg(*p_va, int *); +                if (p_va != NULL) { +                    if (flags & FLAG_SIZE_T) +                        (void) va_arg(*p_va, Py_ssize_t *); +                    else +                        (void) va_arg(*p_va, int *); +                }                  format++;              } else if ((c == 's' || c == 'z' || c == 'y') && *format == '*') {                  format++; @@ -1713,24 +2290,30 @@ skipitem(const char **p_format, va_list *p_va, int flags)          {              if (*format == '!') {                  format++; -                (void) va_arg(*p_va, PyTypeObject*); -                (void) va_arg(*p_va, PyObject **); +                if (p_va != NULL) { +                    (void) va_arg(*p_va, PyTypeObject*); +                    (void) va_arg(*p_va, PyObject **); +                }              }              else if (*format == '&') {                  typedef int (*converter)(PyObject *, void *); -                (void) va_arg(*p_va, converter); -                (void) va_arg(*p_va, void *); +                if (p_va != NULL) { +                    (void) va_arg(*p_va, converter); +                    (void) va_arg(*p_va, void *); +                }                  format++;              }              else { -                (void) va_arg(*p_va, PyObject **); +                if (p_va != NULL) { +                    (void) va_arg(*p_va, PyObject **); +                }              }              break;          }      case '(':           /* bypass tuple, not handled at all previously */          { -            char *msg; +            const char *msg;              for (;;) {                  if (*format==')')                      break; @@ -1766,16 +2349,9 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m      PyObject **o;      va_list vargs; -#ifdef HAVE_STDARG_PROTOTYPES -    va_start(vargs, max); -#else -    va_start(vargs); -#endif -      assert(min >= 0);      assert(min <= max);      if (!PyTuple_Check(args)) { -        va_end(vargs);          PyErr_SetString(PyExc_SystemError,              "PyArg_UnpackTuple() argument list is not a tuple");          return 0; @@ -1793,9 +2369,10 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m                  "unpacked tuple should have %s%zd elements,"                  " but has %zd",                  (min == max ? "" : "at least "), min, l); -        va_end(vargs);          return 0;      } +    if (l == 0) +        return 1;      if (l > max) {          if (name != NULL)              PyErr_Format( @@ -1808,9 +2385,14 @@ PyArg_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t m                  "unpacked tuple should have %s%zd elements,"                  " but has %zd",                  (min == max ? "" : "at most "), max, l); -        va_end(vargs);          return 0;      } + +#ifdef HAVE_STDARG_PROTOTYPES +    va_start(vargs, max); +#else +    va_start(vargs); +#endif      for (i = 0; i < l; i++) {          o = va_arg(vargs, PyObject **);          *o = PyTuple_GET_ITEM(args, i); @@ -1860,6 +2442,19 @@ _PyArg_NoPositional(const char *funcname, PyObject *args)      return 0;  } +void +_PyArg_Fini(void) +{ +    struct _PyArg_Parser *tmp, *s = static_arg_parsers; +    while (s) { +        tmp = s->next; +        s->next = NULL; +        parser_clear(s); +        s = tmp; +    } +    static_arg_parsers = NULL; +} +  #ifdef __cplusplus  };  #endif | 
