summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2024-11-07 21:40:03 (GMT)
committerGitHub <noreply@github.com>2024-11-07 21:40:03 (GMT)
commit1f777396f52a4cf7417f56097f10add8042295f4 (patch)
treed2b16073b0665beba75e900fff1dd2daf24b0717 /Python
parent09d6f5dc7824c74672add512619e978844ff8051 (diff)
downloadcpython-1f777396f52a4cf7417f56097f10add8042295f4.zip
cpython-1f777396f52a4cf7417f56097f10add8042295f4.tar.gz
cpython-1f777396f52a4cf7417f56097f10add8042295f4.tar.bz2
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".
Diffstat (limited to 'Python')
-rw-r--r--Python/clinic/bltinmodule.c.h31
-rw-r--r--Python/getargs.c168
2 files changed, 28 insertions, 171 deletions
diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h
index f75a8d4..b472796 100644
--- a/Python/clinic/bltinmodule.c.h
+++ b/Python/clinic/bltinmodule.c.h
@@ -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]*/
diff --git a/Python/getargs.c b/Python/getargs.c
index a764343..d529994 100644
--- a/Python/getargs.c
+++ b/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, &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)
{