diff options
author | Batuhan Taskaya <batuhan@python.org> | 2021-07-16 15:43:02 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-16 15:43:02 (GMT) |
commit | 9af34c935185eca497617a216d141c72ffaeae9c (patch) | |
tree | 06c965d71e81caaec5193a5220709136b2f5e8d3 /Python | |
parent | 7915c96ffd7ddc5cb6d54015ee4c31255a416892 (diff) | |
download | cpython-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.c | 80 | ||||
-rw-r--r-- | Python/clinic/bltinmodule.c.h | 76 | ||||
-rw-r--r-- | Python/getargs.c | 160 |
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) |