mirror of
https://github.com/python/cpython.git
synced 2025-07-18 08:45:20 +00:00
bpo-20201: variadic arguments support for AC (GH-18609)
Implement support for `*args` in AC, and port `print()` to use it.
This commit is contained in:
parent
7915c96ffd
commit
9af34c9351
8 changed files with 664 additions and 100 deletions
|
@ -1952,24 +1952,31 @@ builtin_pow_impl(PyObject *module, PyObject *base, PyObject *exp,
|
|||
return PyNumber_Power(base, exp, mod);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
print as builtin_print
|
||||
|
||||
*args: object
|
||||
sep: object(c_default="Py_None") = ' '
|
||||
string inserted between values, default a space.
|
||||
end: object(c_default="Py_None") = '\n'
|
||||
string appended after the last value, default a newline.
|
||||
file: object = None
|
||||
a file-like object (stream); defaults to the current sys.stdout.
|
||||
flush: bool = False
|
||||
whether to forcibly flush the stream.
|
||||
|
||||
Prints the values to a stream, or to sys.stdout by default.
|
||||
|
||||
[clinic start generated code]*/
|
||||
|
||||
/* AC: cannot convert yet, waiting for *args support */
|
||||
static PyObject *
|
||||
builtin_print(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep,
|
||||
PyObject *end, PyObject *file, int flush)
|
||||
/*[clinic end generated code: output=3cfc0940f5bc237b input=c143c575d24fe665]*/
|
||||
{
|
||||
static const char * const _keywords[] = {"sep", "end", "file", "flush", 0};
|
||||
static struct _PyArg_Parser _parser = {"|OOOp:print", _keywords, 0};
|
||||
PyObject *sep = NULL, *end = NULL, *file = NULL;
|
||||
int flush = 0;
|
||||
int i, err;
|
||||
|
||||
if (kwnames != NULL &&
|
||||
!_PyArg_ParseStackAndKeywords(args + nargs, 0, kwnames, &_parser,
|
||||
&sep, &end, &file, &flush)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (file == NULL || file == Py_None) {
|
||||
if (file == Py_None) {
|
||||
file = _PySys_GetObjectId(&PyId_stdout);
|
||||
if (file == NULL) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "lost sys.stdout");
|
||||
|
@ -1977,8 +1984,9 @@ builtin_print(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject
|
|||
}
|
||||
|
||||
/* sys.stdout may be None when FILE* stdout isn't connected */
|
||||
if (file == Py_None)
|
||||
if (file == Py_None) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (sep == Py_None) {
|
||||
|
@ -2000,48 +2008,45 @@ builtin_print(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject
|
|||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nargs; i++) {
|
||||
for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
|
||||
if (i > 0) {
|
||||
if (sep == NULL)
|
||||
if (sep == NULL) {
|
||||
err = PyFile_WriteString(" ", file);
|
||||
else
|
||||
err = PyFile_WriteObject(sep, file,
|
||||
Py_PRINT_RAW);
|
||||
if (err)
|
||||
}
|
||||
else {
|
||||
err = PyFile_WriteObject(sep, file, Py_PRINT_RAW);
|
||||
}
|
||||
if (err) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
err = PyFile_WriteObject(args[i], file, Py_PRINT_RAW);
|
||||
if (err)
|
||||
err = PyFile_WriteObject(PyTuple_GET_ITEM(args, i), file, Py_PRINT_RAW);
|
||||
if (err) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (end == NULL)
|
||||
if (end == NULL) {
|
||||
err = PyFile_WriteString("\n", file);
|
||||
else
|
||||
}
|
||||
else {
|
||||
err = PyFile_WriteObject(end, file, Py_PRINT_RAW);
|
||||
if (err)
|
||||
}
|
||||
if (err) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (flush) {
|
||||
PyObject *tmp = _PyObject_CallMethodIdNoArgs(file, &PyId_flush);
|
||||
if (tmp == NULL)
|
||||
if (tmp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(tmp);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(print_doc,
|
||||
"print(value, ..., sep=' ', end='\\n', file=sys.stdout, flush=False)\n\
|
||||
\n\
|
||||
Prints the values to a stream, or to sys.stdout by default.\n\
|
||||
Optional keyword arguments:\n\
|
||||
file: a file-like object (stream); defaults to the current sys.stdout.\n\
|
||||
sep: string inserted between values, default a space.\n\
|
||||
end: string appended after the last value, default a newline.\n\
|
||||
flush: whether to forcibly flush the stream.");
|
||||
|
||||
|
||||
/*[clinic input]
|
||||
input as builtin_input
|
||||
|
@ -2644,7 +2649,6 @@ builtin_issubclass_impl(PyObject *module, PyObject *cls,
|
|||
return PyBool_FromLong(retval);
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
Py_ssize_t tuplesize;
|
||||
|
@ -2955,7 +2959,7 @@ static PyMethodDef builtin_methods[] = {
|
|||
BUILTIN_OCT_METHODDEF
|
||||
BUILTIN_ORD_METHODDEF
|
||||
BUILTIN_POW_METHODDEF
|
||||
{"print", (PyCFunction)(void(*)(void))builtin_print, METH_FASTCALL | METH_KEYWORDS, print_doc},
|
||||
BUILTIN_PRINT_METHODDEF
|
||||
BUILTIN_REPR_METHODDEF
|
||||
BUILTIN_ROUND_METHODDEF
|
||||
BUILTIN_SETATTR_METHODDEF
|
||||
|
|
76
Python/clinic/bltinmodule.c.h
generated
76
Python/clinic/bltinmodule.c.h
generated
|
@ -674,6 +674,80 @@ exit:
|
|||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(builtin_print__doc__,
|
||||
"print($module, /, *args, sep=\' \', end=\'\\n\', file=None, flush=False)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Prints the values to a stream, or to sys.stdout by default.\n"
|
||||
"\n"
|
||||
" sep\n"
|
||||
" string inserted between values, default a space.\n"
|
||||
" end\n"
|
||||
" string appended after the last value, default a newline.\n"
|
||||
" file\n"
|
||||
" a file-like object (stream); defaults to the current sys.stdout.\n"
|
||||
" flush\n"
|
||||
" whether to forcibly flush the stream.");
|
||||
|
||||
#define BUILTIN_PRINT_METHODDEF \
|
||||
{"print", (PyCFunction)(void(*)(void))builtin_print, METH_FASTCALL|METH_KEYWORDS, builtin_print__doc__},
|
||||
|
||||
static PyObject *
|
||||
builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep,
|
||||
PyObject *end, PyObject *file, int flush);
|
||||
|
||||
static PyObject *
|
||||
builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
static const char * const _keywords[] = {"sep", "end", "file", "flush", NULL};
|
||||
static _PyArg_Parser _parser = {NULL, _keywords, "print", 0};
|
||||
PyObject *argsbuf[5];
|
||||
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
|
||||
PyObject *__clinic_args = NULL;
|
||||
PyObject *sep = Py_None;
|
||||
PyObject *end = Py_None;
|
||||
PyObject *file = Py_None;
|
||||
int flush = 0;
|
||||
|
||||
args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf);
|
||||
if (!args) {
|
||||
goto exit;
|
||||
}
|
||||
__clinic_args = args[0];
|
||||
if (!noptargs) {
|
||||
goto skip_optional_kwonly;
|
||||
}
|
||||
if (args[1]) {
|
||||
sep = args[1];
|
||||
if (!--noptargs) {
|
||||
goto skip_optional_kwonly;
|
||||
}
|
||||
}
|
||||
if (args[2]) {
|
||||
end = args[2];
|
||||
if (!--noptargs) {
|
||||
goto skip_optional_kwonly;
|
||||
}
|
||||
}
|
||||
if (args[3]) {
|
||||
file = args[3];
|
||||
if (!--noptargs) {
|
||||
goto skip_optional_kwonly;
|
||||
}
|
||||
}
|
||||
flush = PyObject_IsTrue(args[4]);
|
||||
if (flush < 0) {
|
||||
goto exit;
|
||||
}
|
||||
skip_optional_kwonly:
|
||||
return_value = builtin_print_impl(module, __clinic_args, sep, end, file, flush);
|
||||
|
||||
exit:
|
||||
Py_XDECREF(__clinic_args);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(builtin_input__doc__,
|
||||
"input($module, prompt=None, /)\n"
|
||||
"--\n"
|
||||
|
@ -877,4 +951,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=e1d8057298b5de61 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=77ace832b3fb38e0 input=a9049054013a1b77]*/
|
||||
|
|
160
Python/getargs.c
160
Python/getargs.c
|
@ -2465,6 +2465,166 @@ _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 *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 (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) {
|
||||
Py_INCREF(args[i]);
|
||||
PyTuple_SET_ITEM(buf[vararg], i - vararg, args[i]);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
buf[i] = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* copy keyword args using kwtuple to drive process */
|
||||
for (i = Py_MAX((int)nargs, posonly) - varargssize; 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()) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
current_arg = find_keyword(kwnames, kwstack, keyword);
|
||||
}
|
||||
}
|
||||
else {
|
||||
current_arg = NULL;
|
||||
}
|
||||
|
||||
buf[i + vararg + 1] = 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);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (nkwargs > 0) {
|
||||
Py_ssize_t j;
|
||||
/* 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++;
|
||||
}
|
||||
|
||||
match = PySequence_Contains(kwtuple, keyword);
|
||||
if (match <= 0) {
|
||||
if (!match) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%S' is an invalid keyword "
|
||||
"argument for %.200s%s",
|
||||
keyword,
|
||||
(parser->fname == NULL) ? "this function" : parser->fname,
|
||||
(parser->fname == NULL) ? "" : "()");
|
||||
}
|
||||
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