mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
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:
parent
09d6f5dc78
commit
1f777396f5
22 changed files with 1597 additions and 662 deletions
31
Python/clinic/bltinmodule.c.h
generated
31
Python/clinic/bltinmodule.c.h
generated
|
@ -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]*/
|
||||
|
|
168
Python/getargs.c
168
Python/getargs.c
|
@ -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, ¤t_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)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue