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:
Batuhan Taskaya 2021-07-16 18:43:02 +03:00 committed by GitHub
parent 7915c96ffd
commit 9af34c9351
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 664 additions and 100 deletions

View file

@ -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

View file

@ -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]*/

View file

@ -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)