gh-122943: Rework support of var-positional parameter in Argument Clinic (GH-122945)

Move creation of a tuple for var-positional parameter out of
_PyArg_UnpackKeywordsWithVararg().
Merge _PyArg_UnpackKeywordsWithVararg() with _PyArg_UnpackKeywords().
Add a new parameter in _PyArg_UnpackKeywords().

The "parameters" and "converters" attributes of ParseArgsCodeGen no
longer contain the var-positional parameter. It is now available as the
"varpos" attribute. Optimize code generation for var-positional
parameter and reuse the same generating code for functions with and without
keyword parameters.

Add special converters for var-positional parameter. "tuple" represents it as
a Python tuple and "array" represents it as a continuous array of PyObject*.
"object" is a temporary alias of "tuple".
This commit is contained in:
Serhiy Storchaka 2024-11-07 23:40:03 +02:00 committed by GitHub
parent 09d6f5dc78
commit 1f777396f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 1597 additions and 662 deletions

View file

@ -7,6 +7,7 @@ preserve
# include "pycore_runtime.h" // _Py_ID()
#endif
#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
#include "pycore_tuple.h" // _PyTuple_FromArray()
PyDoc_STRVAR(builtin___import____doc__,
"__import__($module, /, name, globals=None, locals=None, fromlist=(),\n"
@ -933,7 +934,8 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[5];
PyObject *argsbuf[4];
PyObject * const *fastargs;
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
PyObject *__clinic_args = NULL;
PyObject *sep = Py_None;
@ -941,41 +943,46 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec
PyObject *file = Py_None;
int flush = 0;
args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf);
if (!args) {
fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf);
if (!fastargs) {
goto exit;
}
__clinic_args = args[0];
if (!noptargs) {
goto skip_optional_kwonly;
}
if (args[1]) {
sep = args[1];
if (fastargs[0]) {
sep = fastargs[0];
if (!--noptargs) {
goto skip_optional_kwonly;
}
}
if (args[2]) {
end = args[2];
if (fastargs[1]) {
end = fastargs[1];
if (!--noptargs) {
goto skip_optional_kwonly;
}
}
if (args[3]) {
file = args[3];
if (fastargs[2]) {
file = fastargs[2];
if (!--noptargs) {
goto skip_optional_kwonly;
}
}
flush = PyObject_IsTrue(args[4]);
flush = PyObject_IsTrue(fastargs[3]);
if (flush < 0) {
goto exit;
}
skip_optional_kwonly:
__clinic_args = _PyTuple_FromArray(args, nargs);
if (__clinic_args == NULL) {
goto exit;
}
return_value = builtin_print_impl(module, __clinic_args, sep, end, file, flush);
exit:
/* Cleanup for args */
Py_XDECREF(__clinic_args);
return return_value;
}
@ -1228,4 +1235,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
exit:
return return_value;
}
/*[clinic end generated code: output=435d3f286a863c49 input=a9049054013a1b77]*/
/*[clinic end generated code: output=76b27cf4164f257e input=a9049054013a1b77]*/

View file

@ -2308,13 +2308,11 @@ vgetargskeywordsfast(PyObject *args, PyObject *keywords,
}
#undef _PyArg_UnpackKeywords
PyObject * const *
_PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs,
_PyArg_UnpackKeywordsEx(PyObject *const *args, Py_ssize_t nargs,
PyObject *kwargs, PyObject *kwnames,
struct _PyArg_Parser *parser,
int minpos, int maxpos, int minkw,
int minpos, int maxpos, int minkw, int varpos,
PyObject **buf)
{
PyObject *kwtuple;
@ -2360,11 +2358,11 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs,
else {
nkwargs = 0;
}
if (nkwargs == 0 && minkw == 0 && minpos <= nargs && nargs <= maxpos) {
if (nkwargs == 0 && minkw == 0 && minpos <= nargs && (varpos || nargs <= maxpos)) {
/* Fast path. */
return args;
}
if (nargs + nkwargs > maxargs) {
if (!varpos && nargs + nkwargs > maxargs) {
/* Adding "keyword" (when nargs == 0) prevents producing wrong error
messages in some special cases (see bpo-31229). */
PyErr_Format(PyExc_TypeError,
@ -2377,7 +2375,7 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs,
nargs + nkwargs);
return NULL;
}
if (nargs > maxpos) {
if (!varpos && nargs > maxpos) {
if (maxpos == 0) {
PyErr_Format(PyExc_TypeError,
"%.200s%s takes no positional arguments",
@ -2402,13 +2400,16 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs,
" (%zd given)",
(parser->fname == NULL) ? "function" : parser->fname,
(parser->fname == NULL) ? "" : "()",
minposonly < maxpos ? "at least" : "exactly",
(varpos || minposonly < maxpos) ? "at least" : "exactly",
minposonly,
minposonly == 1 ? "" : "s",
nargs);
return NULL;
}
if (varpos) {
nargs = Py_MIN(maxpos, nargs);
}
/* copy tuple args */
for (i = 0; i < nargs; i++) {
buf[i] = args[i];
@ -2486,157 +2487,6 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs,
return buf;
}
PyObject * const *
_PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs,
PyObject *kwargs, PyObject *kwnames,
struct _PyArg_Parser *parser,
int minpos, int maxpos, int minkw,
int vararg, PyObject **buf)
{
PyObject *kwtuple;
PyObject *keyword;
Py_ssize_t varargssize = 0;
int i, posonly, minposonly, maxargs;
int reqlimit = minkw ? maxpos + minkw : minpos;
Py_ssize_t nkwargs;
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) < 0) {
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 (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;
}
/* create varargs tuple */
varargssize = nargs - maxpos;
if (varargssize < 0) {
varargssize = 0;
}
buf[vararg] = PyTuple_New(varargssize);
if (!buf[vararg]) {
return NULL;
}
/* copy tuple args */
for (i = 0; i < nargs; i++) {
if (i >= vararg) {
PyTuple_SET_ITEM(buf[vararg], i - vararg, Py_NewRef(args[i]));
continue;
}
else {
buf[i] = args[i];
}
}
/* copy keyword args using kwtuple to drive process */
for (i = Py_MAX((int)nargs, posonly) - Py_SAFE_DOWNCAST(varargssize, Py_ssize_t, int); i < maxargs; i++) {
PyObject *current_arg;
if (nkwargs) {
keyword = PyTuple_GET_ITEM(kwtuple, i - posonly);
if (kwargs != NULL) {
if (PyDict_GetItemRef(kwargs, keyword, &current_arg) < 0) {
goto exit;
}
}
else {
current_arg = find_keyword(kwnames, kwstack, keyword);
}
}
else {
current_arg = NULL;
}
/* If an arguments is passed in as a keyword argument,
* it should be placed before `buf[vararg]`.
*
* For example:
* def f(a, /, b, *args):
* pass
* f(1, b=2)
*
* This `buf` array should be: [1, 2, NULL].
* In this case, nargs < vararg.
*
* Otherwise, we leave a place at `buf[vararg]` for vararg tuple
* so the index is `i + 1`. */
if (i < vararg) {
buf[i] = current_arg;
}
else {
buf[i + 1] = current_arg;
}
if (current_arg) {
Py_DECREF(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);
goto exit;
}
}
if (nkwargs > 0) {
error_unexpected_keyword_arg(kwargs, kwnames, kwtuple, parser->fname);
goto exit;
}
return buf;
exit:
Py_XDECREF(buf[vararg]);
return NULL;
}
static const char *
skipitem(const char **p_format, va_list *p_va, int flags)
{