summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorBatuhan Taskaya <batuhan@python.org>2021-07-16 15:43:02 (GMT)
committerGitHub <noreply@github.com>2021-07-16 15:43:02 (GMT)
commit9af34c935185eca497617a216d141c72ffaeae9c (patch)
tree06c965d71e81caaec5193a5220709136b2f5e8d3 /Python
parent7915c96ffd7ddc5cb6d54015ee4c31255a416892 (diff)
downloadcpython-9af34c935185eca497617a216d141c72ffaeae9c.zip
cpython-9af34c935185eca497617a216d141c72ffaeae9c.tar.gz
cpython-9af34c935185eca497617a216d141c72ffaeae9c.tar.bz2
bpo-20201: variadic arguments support for AC (GH-18609)
Implement support for `*args` in AC, and port `print()` to use it.
Diffstat (limited to 'Python')
-rw-r--r--Python/bltinmodule.c80
-rw-r--r--Python/clinic/bltinmodule.c.h76
-rw-r--r--Python/getargs.c160
3 files changed, 277 insertions, 39 deletions
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 66c5fba..bfe21ad 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -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
diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h
index 4ea5876..1fade99 100644
--- a/Python/clinic/bltinmodule.c.h
+++ b/Python/clinic/bltinmodule.c.h
@@ -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]*/
diff --git a/Python/getargs.c b/Python/getargs.c
index d5e0835..330f2b4 100644
--- a/Python/getargs.c
+++ b/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)