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

This commit is contained in:
Serhiy Storchaka 2019-03-14 10:32:22 +02:00 committed by GitHub
parent 2c0d3f4547
commit 3191391515
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
65 changed files with 7466 additions and 868 deletions

View file

@ -20,19 +20,55 @@ warnings_warn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec
{
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]*/

View file

@ -180,7 +180,9 @@ builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj
{
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 @@ builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj
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 @@ builtin_round(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec
{
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 @@ builtin_sum(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
{
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 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
exit:
return return_value;
}
/*[clinic end generated code: output=00b97a48ea49eaf2 input=a9049054013a1b77]*/
/*[clinic end generated code: output=3f690311ac556c31 input=a9049054013a1b77]*/

View file

@ -411,12 +411,29 @@ _imp_source_hash(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb
{
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 @@ exit:
#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]*/

View file

@ -410,11 +410,21 @@ sys_set_coroutine_origin_tracking_depth(PyObject *module, PyObject *const *args,
{
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 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored))
#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]*/

View file

@ -17,14 +17,41 @@ tb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
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 @@ tb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
/*[clinic end generated code: output=0133130d7d19556f input=a9049054013a1b77]*/
/*[clinic end generated code: output=7e4c0e252d0973b0 input=a9049054013a1b77]*/

View file

@ -1905,24 +1905,11 @@ parser_init(struct _PyArg_Parser *parser)
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 @@ parser_init(struct _PyArg_Parser *parser)
}
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 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
}
#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)
{