bpo-36127: Argument Clinic: inline parsing code for keyword parameters. (GH-12058)

diff --git a/Python/clinic/_warnings.c.h b/Python/clinic/_warnings.c.h
index d1e50de..67ab0e3 100644
--- a/Python/clinic/_warnings.c.h
+++ b/Python/clinic/_warnings.c.h
@@ -20,19 +20,55 @@
 {
     PyObject *return_value = NULL;
     static const char * const _keywords[] = {"message", "category", "stacklevel", "source", NULL};
-    static _PyArg_Parser _parser = {"O|OnO:warn", _keywords, 0};
+    static _PyArg_Parser _parser = {NULL, _keywords, "warn", 0};
+    PyObject *argsbuf[4];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
     PyObject *message;
     PyObject *category = Py_None;
     Py_ssize_t stacklevel = 1;
     PyObject *source = Py_None;
 
-    if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        &message, &category, &stacklevel, &source)) {
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf);
+    if (!args) {
         goto exit;
     }
+    message = args[0];
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    if (args[1]) {
+        category = args[1];
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+    if (args[2]) {
+        if (PyFloat_Check(args[2])) {
+            PyErr_SetString(PyExc_TypeError,
+                            "integer argument expected, got float" );
+            goto exit;
+        }
+        {
+            Py_ssize_t ival = -1;
+            PyObject *iobj = PyNumber_Index(args[2]);
+            if (iobj != NULL) {
+                ival = PyLong_AsSsize_t(iobj);
+                Py_DECREF(iobj);
+            }
+            if (ival == -1 && PyErr_Occurred()) {
+                goto exit;
+            }
+            stacklevel = ival;
+        }
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+    source = args[3];
+skip_optional_pos:
     return_value = warnings_warn_impl(module, message, category, stacklevel, source);
 
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=a4fbe6e2d1cc2091 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b7bb54c73b5433ec input=a9049054013a1b77]*/
diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h
index df99709..0ed11bc 100644
--- a/Python/clinic/bltinmodule.c.h
+++ b/Python/clinic/bltinmodule.c.h
@@ -180,7 +180,9 @@
 {
     PyObject *return_value = NULL;
     static const char * const _keywords[] = {"source", "filename", "mode", "flags", "dont_inherit", "optimize", "feature_version", NULL};
-    static _PyArg_Parser _parser = {"OO&s|iiii:compile", _keywords, 0};
+    static _PyArg_Parser _parser = {NULL, _keywords, "compile", 0};
+    PyObject *argsbuf[7];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 3;
     PyObject *source;
     PyObject *filename;
     const char *mode;
@@ -189,10 +191,82 @@
     int optimize = -1;
     int feature_version = -1;
 
-    if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        &source, PyUnicode_FSDecoder, &filename, &mode, &flags, &dont_inherit, &optimize, &feature_version)) {
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 7, 0, argsbuf);
+    if (!args) {
         goto exit;
     }
+    source = args[0];
+    if (!PyUnicode_FSDecoder(args[1], &filename)) {
+        goto exit;
+    }
+    if (!PyUnicode_Check(args[2])) {
+        _PyArg_BadArgument("compile", 3, "str", args[2]);
+        goto exit;
+    }
+    Py_ssize_t mode_length;
+    mode = PyUnicode_AsUTF8AndSize(args[2], &mode_length);
+    if (mode == NULL) {
+        goto exit;
+    }
+    if (strlen(mode) != (size_t)mode_length) {
+        PyErr_SetString(PyExc_ValueError, "embedded null character");
+        goto exit;
+    }
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    if (args[3]) {
+        if (PyFloat_Check(args[3])) {
+            PyErr_SetString(PyExc_TypeError,
+                            "integer argument expected, got float" );
+            goto exit;
+        }
+        flags = _PyLong_AsInt(args[3]);
+        if (flags == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+    if (args[4]) {
+        if (PyFloat_Check(args[4])) {
+            PyErr_SetString(PyExc_TypeError,
+                            "integer argument expected, got float" );
+            goto exit;
+        }
+        dont_inherit = _PyLong_AsInt(args[4]);
+        if (dont_inherit == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+    if (args[5]) {
+        if (PyFloat_Check(args[5])) {
+            PyErr_SetString(PyExc_TypeError,
+                            "integer argument expected, got float" );
+            goto exit;
+        }
+        optimize = _PyLong_AsInt(args[5]);
+        if (optimize == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+    if (PyFloat_Check(args[6])) {
+        PyErr_SetString(PyExc_TypeError,
+                        "integer argument expected, got float" );
+        goto exit;
+    }
+    feature_version = _PyLong_AsInt(args[6]);
+    if (feature_version == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+skip_optional_pos:
     return_value = builtin_compile_impl(module, source, filename, mode, flags, dont_inherit, optimize, feature_version);
 
 exit:
@@ -637,14 +711,22 @@
 {
     PyObject *return_value = NULL;
     static const char * const _keywords[] = {"number", "ndigits", NULL};
-    static _PyArg_Parser _parser = {"O|O:round", _keywords, 0};
+    static _PyArg_Parser _parser = {NULL, _keywords, "round", 0};
+    PyObject *argsbuf[2];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
     PyObject *number;
     PyObject *ndigits = NULL;
 
-    if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        &number, &ndigits)) {
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf);
+    if (!args) {
         goto exit;
     }
+    number = args[0];
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    ndigits = args[1];
+skip_optional_pos:
     return_value = builtin_round_impl(module, number, ndigits);
 
 exit:
@@ -672,14 +754,22 @@
 {
     PyObject *return_value = NULL;
     static const char * const _keywords[] = {"", "start", NULL};
-    static _PyArg_Parser _parser = {"O|O:sum", _keywords, 0};
+    static _PyArg_Parser _parser = {NULL, _keywords, "sum", 0};
+    PyObject *argsbuf[2];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
     PyObject *iterable;
     PyObject *start = NULL;
 
-    if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        &iterable, &start)) {
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf);
+    if (!args) {
         goto exit;
     }
+    iterable = args[0];
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    start = args[1];
+skip_optional_pos:
     return_value = builtin_sum_impl(module, iterable, start);
 
 exit:
@@ -755,4 +845,4 @@
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=00b97a48ea49eaf2 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=3f690311ac556c31 input=a9049054013a1b77]*/
diff --git a/Python/clinic/import.c.h b/Python/clinic/import.c.h
index 9ee20fb..05e3106 100644
--- a/Python/clinic/import.c.h
+++ b/Python/clinic/import.c.h
@@ -411,12 +411,29 @@
 {
     PyObject *return_value = NULL;
     static const char * const _keywords[] = {"key", "source", NULL};
-    static _PyArg_Parser _parser = {"ly*:source_hash", _keywords, 0};
+    static _PyArg_Parser _parser = {NULL, _keywords, "source_hash", 0};
+    PyObject *argsbuf[2];
     long key;
     Py_buffer source = {NULL, NULL};
 
-    if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        &key, &source)) {
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf);
+    if (!args) {
+        goto exit;
+    }
+    if (PyFloat_Check(args[0])) {
+        PyErr_SetString(PyExc_TypeError,
+                        "integer argument expected, got float" );
+        goto exit;
+    }
+    key = PyLong_AsLong(args[0]);
+    if (key == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    if (PyObject_GetBuffer(args[1], &source, PyBUF_SIMPLE) != 0) {
+        goto exit;
+    }
+    if (!PyBuffer_IsContiguous(&source, 'C')) {
+        _PyArg_BadArgument("source_hash", 2, "contiguous buffer", args[1]);
         goto exit;
     }
     return_value = _imp_source_hash_impl(module, key, &source);
@@ -437,4 +454,4 @@
 #ifndef _IMP_EXEC_DYNAMIC_METHODDEF
     #define _IMP_EXEC_DYNAMIC_METHODDEF
 #endif /* !defined(_IMP_EXEC_DYNAMIC_METHODDEF) */
-/*[clinic end generated code: output=2409b8feeafe7c4b input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b51244770fdcf4b8 input=a9049054013a1b77]*/
diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h
index fc9794b..c70b721 100644
--- a/Python/clinic/sysmodule.c.h
+++ b/Python/clinic/sysmodule.c.h
@@ -410,11 +410,21 @@
 {
     PyObject *return_value = NULL;
     static const char * const _keywords[] = {"depth", NULL};
-    static _PyArg_Parser _parser = {"i:set_coroutine_origin_tracking_depth", _keywords, 0};
+    static _PyArg_Parser _parser = {NULL, _keywords, "set_coroutine_origin_tracking_depth", 0};
+    PyObject *argsbuf[1];
     int depth;
 
-    if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        &depth)) {
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+    if (!args) {
+        goto exit;
+    }
+    if (PyFloat_Check(args[0])) {
+        PyErr_SetString(PyExc_TypeError,
+                        "integer argument expected, got float" );
+        goto exit;
+    }
+    depth = _PyLong_AsInt(args[0]);
+    if (depth == -1 && PyErr_Occurred()) {
         goto exit;
     }
     return_value = sys_set_coroutine_origin_tracking_depth_impl(module, depth);
@@ -1050,4 +1060,4 @@
 #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
     #define SYS_GETANDROIDAPILEVEL_METHODDEF
 #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
-/*[clinic end generated code: output=109787af3401cd27 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=3ba4c194d00f1866 input=a9049054013a1b77]*/
diff --git a/Python/clinic/traceback.c.h b/Python/clinic/traceback.c.h
index d9daccb..2815f65 100644
--- a/Python/clinic/traceback.c.h
+++ b/Python/clinic/traceback.c.h
@@ -17,14 +17,41 @@
 {
     PyObject *return_value = NULL;
     static const char * const _keywords[] = {"tb_next", "tb_frame", "tb_lasti", "tb_lineno", NULL};
-    static _PyArg_Parser _parser = {"OO!ii:TracebackType", _keywords, 0};
+    static _PyArg_Parser _parser = {NULL, _keywords, "TracebackType", 0};
+    PyObject *argsbuf[4];
+    PyObject * const *fastargs;
+    Py_ssize_t nargs = PyTuple_GET_SIZE(args);
     PyObject *tb_next;
     PyFrameObject *tb_frame;
     int tb_lasti;
     int tb_lineno;
 
-    if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
-        &tb_next, &PyFrame_Type, &tb_frame, &tb_lasti, &tb_lineno)) {
+    fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 4, 4, 0, argsbuf);
+    if (!fastargs) {
+        goto exit;
+    }
+    tb_next = fastargs[0];
+    if (!PyObject_TypeCheck(fastargs[1], &PyFrame_Type)) {
+        _PyArg_BadArgument("TracebackType", 2, (&PyFrame_Type)->tp_name, fastargs[1]);
+        goto exit;
+    }
+    tb_frame = (PyFrameObject *)fastargs[1];
+    if (PyFloat_Check(fastargs[2])) {
+        PyErr_SetString(PyExc_TypeError,
+                        "integer argument expected, got float" );
+        goto exit;
+    }
+    tb_lasti = _PyLong_AsInt(fastargs[2]);
+    if (tb_lasti == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    if (PyFloat_Check(fastargs[3])) {
+        PyErr_SetString(PyExc_TypeError,
+                        "integer argument expected, got float" );
+        goto exit;
+    }
+    tb_lineno = _PyLong_AsInt(fastargs[3]);
+    if (tb_lineno == -1 && PyErr_Occurred()) {
         goto exit;
     }
     return_value = tb_new_impl(type, tb_next, tb_frame, tb_lasti, tb_lineno);
@@ -32,4 +59,4 @@
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=0133130d7d19556f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=7e4c0e252d0973b0 input=a9049054013a1b77]*/
diff --git a/Python/getargs.c b/Python/getargs.c
index 05ebe9c..693a29c 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -1905,24 +1905,11 @@
     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++) {
@@ -1938,59 +1925,73 @@
     }
     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) {
+        /* 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;
         }
-        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++;
+        else {
+            parser->custom_msg = strchr(parser->format,';');
+            if (parser->custom_msg)
+                parser->custom_msg++;
         }
-        if (IS_END_OF_FORMAT(*format)) {
+
+        min = max = INT_MAX;
+        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 keyword list entries (%d) than "
-                         "format specifiers (%d)", len, i);
+                "more argument specifiers than keyword list entries "
+                "(remaining format:'%s')", format);
             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;
@@ -2313,6 +2314,215 @@
 }
 
 
+#undef _PyArg_UnpackKeywords
+
+PyObject * const *
+_PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs,
+                      PyObject *kwargs, PyObject *kwnames,
+                      struct _PyArg_Parser *parser,
+                      int minpos, int maxpos, int minkw,
+                      PyObject **buf)
+{
+    PyObject *kwtuple;
+    PyObject *keyword;
+    int i, posonly, minposonly, maxargs;
+    int reqlimit = minkw ? maxpos + minkw : minpos;
+    Py_ssize_t nkwargs;
+    PyObject *current_arg;
+    PyObject * const *kwstack = NULL;
+
+    assert(kwargs == NULL || PyDict_Check(kwargs));
+    assert(kwargs == NULL || kwnames == NULL);
+
+    if (parser == NULL) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
+
+    if (kwnames != NULL && !PyTuple_Check(kwnames)) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
+
+    if (args == NULL && nargs == 0) {
+        args = buf;
+    }
+
+    if (!parser_init(parser)) {
+        return NULL;
+    }
+
+    kwtuple = parser->kwtuple;
+    posonly = parser->pos;
+    minposonly = Py_MIN(posonly, minpos);
+    maxargs = posonly + (int)PyTuple_GET_SIZE(kwtuple);
+
+    if (kwargs != NULL) {
+        nkwargs = PyDict_GET_SIZE(kwargs);
+    }
+    else if (kwnames != NULL) {
+        nkwargs = PyTuple_GET_SIZE(kwnames);
+        kwstack = args + nargs;
+    }
+    else {
+        nkwargs = 0;
+    }
+    if (nkwargs == 0 && minkw == 0 && minpos <= nargs && nargs <= maxpos) {
+        /* Fast path. */
+        return args;
+    }
+    if (nargs + nkwargs > maxargs) {
+        /* Adding "keyword" (when nargs == 0) prevents producing wrong error
+           messages in some special cases (see bpo-31229). */
+        PyErr_Format(PyExc_TypeError,
+                     "%.200s%s takes at most %d %sargument%s (%zd given)",
+                     (parser->fname == NULL) ? "function" : parser->fname,
+                     (parser->fname == NULL) ? "" : "()",
+                     maxargs,
+                     (nargs == 0) ? "keyword " : "",
+                     (maxargs == 1) ? "" : "s",
+                     nargs + nkwargs);
+        return NULL;
+    }
+    if (nargs > maxpos) {
+        if (maxpos == 0) {
+            PyErr_Format(PyExc_TypeError,
+                         "%.200s%s takes no positional arguments",
+                         (parser->fname == NULL) ? "function" : parser->fname,
+                         (parser->fname == NULL) ? "" : "()");
+        }
+        else {
+            PyErr_Format(PyExc_TypeError,
+                         "%.200s%s takes %s %d positional argument%s (%zd given)",
+                         (parser->fname == NULL) ? "function" : parser->fname,
+                         (parser->fname == NULL) ? "" : "()",
+                         (minpos < maxpos) ? "at most" : "exactly",
+                         maxpos,
+                         (maxpos == 1) ? "" : "s",
+                         nargs);
+        }
+        return NULL;
+    }
+    if (nargs < minposonly) {
+        PyErr_Format(PyExc_TypeError,
+                     "%.200s%s takes %s %d positional argument%s"
+                     " (%zd given)",
+                     (parser->fname == NULL) ? "function" : parser->fname,
+                     (parser->fname == NULL) ? "" : "()",
+                     minposonly < maxpos ? "at least" : "exactly",
+                     minposonly,
+                     minposonly == 1 ? "" : "s",
+                     nargs);
+        return NULL;
+    }
+
+    /* copy tuple args */
+    for (i = 0; i < nargs; i++) {
+        buf[i] = args[i];
+    }
+
+    /* copy keyword args using kwtuple to drive process */
+    for (i = Py_MAX(nargs, posonly); i < maxargs; i++) {
+        if (nkwargs) {
+            keyword = PyTuple_GET_ITEM(kwtuple, i - posonly);
+            if (kwargs != NULL) {
+                current_arg = PyDict_GetItemWithError(kwargs, keyword);
+                if (!current_arg && PyErr_Occurred()) {
+                    return NULL;
+                }
+            }
+            else {
+                current_arg = find_keyword(kwnames, kwstack, keyword);
+            }
+        }
+        else if (i >= reqlimit) {
+            break;
+        }
+        else {
+            current_arg = NULL;
+        }
+
+        buf[i] = current_arg;
+
+        if (current_arg) {
+            --nkwargs;
+        }
+        else if (i < minpos || (maxpos <= i && i < reqlimit)) {
+            /* Less arguments than required */
+            keyword = PyTuple_GET_ITEM(kwtuple, i - posonly);
+            PyErr_Format(PyExc_TypeError,  "%.200s%s missing required "
+                         "argument '%U' (pos %d)",
+                         (parser->fname == NULL) ? "function" : parser->fname,
+                         (parser->fname == NULL) ? "" : "()",
+                         keyword, i+1);
+            return NULL;
+        }
+    }
+
+    if (nkwargs > 0) {
+        Py_ssize_t j;
+        /* make sure there are no arguments given by name and position */
+        for (i = posonly; i < nargs; i++) {
+            keyword = PyTuple_GET_ITEM(kwtuple, i - posonly);
+            if (kwargs != NULL) {
+                current_arg = PyDict_GetItemWithError(kwargs, keyword);
+                if (!current_arg && PyErr_Occurred()) {
+                    return NULL;
+                }
+            }
+            else {
+                current_arg = find_keyword(kwnames, kwstack, keyword);
+            }
+            if (current_arg) {
+                /* arg present in tuple and in dict */
+                PyErr_Format(PyExc_TypeError,
+                             "argument for %.200s%s given by name ('%U') "
+                             "and position (%d)",
+                             (parser->fname == NULL) ? "function" : parser->fname,
+                             (parser->fname == NULL) ? "" : "()",
+                             keyword, i+1);
+                return NULL;
+            }
+        }
+        /* 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++;
+            }
+
+            if (!PyUnicode_Check(keyword)) {
+                PyErr_SetString(PyExc_TypeError,
+                                "keywords must be strings");
+                return NULL;
+            }
+            match = PySequence_Contains(kwtuple, keyword);
+            if (match <= 0) {
+                if (!match) {
+                    PyErr_Format(PyExc_TypeError,
+                                 "'%U' is an invalid keyword "
+                                 "argument for %.200s%s",
+                                 keyword,
+                                 (parser->fname == NULL) ? "this function" : parser->fname,
+                                 (parser->fname == NULL) ? "" : "()");
+                }
+                return NULL;
+            }
+        }
+    }
+
+    return buf;
+}
+
+
 static const char *
 skipitem(const char **p_format, va_list *p_va, int flags)
 {