From b68176d081e19a3cedbaf2cdb31ecd7690421ec8 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Fri, 31 Dec 2021 16:37:07 +0800 Subject: [PATCH 01/22] Avoid temporary `varargs` tuple creation in argument passing --- Include/modsupport.h | 3 +- Lib/test/clinic.test | 67 +++++++++++++++++++++++------------------- Python/getargs.c | 26 ++++------------ Tools/clinic/clinic.py | 14 ++++++--- 4 files changed, 55 insertions(+), 55 deletions(-) diff --git a/Include/modsupport.h b/Include/modsupport.h index baf47f0038d08d..bc84ae4d1c261f 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -134,7 +134,8 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg( PyObject *kwargs, PyObject *kwnames, struct _PyArg_Parser *parser, int minpos, int maxpos, int minkw, - int vararg, PyObject **buf); + int vararg, Py_ssize_t varargssize, + PyObject **buf); #define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index d2934b3f211acf..896ad095073492 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3324,14 +3324,15 @@ PyDoc_STRVAR(test_vararg_and_posonly__doc__, {"test_vararg_and_posonly", (PyCFunction)(void(*)(void))test_vararg_and_posonly, METH_FASTCALL, test_vararg_and_posonly__doc__}, static PyObject * -test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args); +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args); static PyObject * test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; if (!_PyArg_CheckPositional("test_vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) { goto exit; @@ -3341,16 +3342,16 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg for (Py_ssize_t i = 0; i < nargs - 1; ++i) { PyTuple_SET_ITEM(__clinic_args, i, args[1 + i]); } - return_value = test_vararg_and_posonly_impl(module, a, __clinic_args); + return_value = test_vararg_and_posonly_impl(module, a, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=ada613d2d87c9341 input=08dc2bf7afbf1613]*/ +test_vararg_and_posonly_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args) +/*[clinic end generated code: output=6a6c69c6ff7deb9b input=08dc2bf7afbf1613]*/ /*[clinic input] test_vararg @@ -3370,7 +3371,8 @@ PyDoc_STRVAR(test_vararg__doc__, {"test_vararg", (PyCFunction)(void(*)(void))test_vararg, METH_FASTCALL|METH_KEYWORDS, test_vararg__doc__}, static PyObject * -test_vararg_impl(PyObject *module, PyObject *a, PyObject *args); +test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, + PyObject *const *args); static PyObject * test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -3379,26 +3381,27 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject static const char * const _keywords[] = {"a", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg", 0}; PyObject *argsbuf[2]; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); if (!args) { goto exit; } a = args[0]; - __clinic_args = args[1]; - return_value = test_vararg_impl(module, a, __clinic_args); + __clinic_args = (PyObject *const *)args[1]; + return_value = test_vararg_impl(module, a, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=f721025731c3bfe8 input=81d33815ad1bae6e]*/ +test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=9f3d65086405e9b6 input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -3419,7 +3422,8 @@ PyDoc_STRVAR(test_vararg_with_default__doc__, {"test_vararg_with_default", (PyCFunction)(void(*)(void))test_vararg_with_default, METH_FASTCALL|METH_KEYWORDS, test_vararg_with_default__doc__}, static PyObject * -test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, +test_vararg_with_default_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args, int b); static PyObject * @@ -3429,17 +3433,18 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar static const char * const _keywords[] = {"a", "b", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_default", 0}; PyObject *argsbuf[3]; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; int b = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); if (!args) { goto exit; } a = args[0]; - __clinic_args = args[1]; + __clinic_args = (PyObject *const *)args[1]; if (!noptargs) { goto skip_optional_kwonly; } @@ -3448,17 +3453,17 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar goto exit; } skip_optional_kwonly: - return_value = test_vararg_with_default_impl(module, a, __clinic_args, b); + return_value = test_vararg_with_default_impl(module, a, varargssize, __clinic_args, b); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, +test_vararg_with_default_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=63b34d3241c52fda input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=711a606d86d09fec input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -3479,7 +3484,8 @@ PyDoc_STRVAR(test_vararg_with_only_defaults__doc__, {"test_vararg_with_only_defaults", (PyCFunction)(void(*)(void))test_vararg_with_only_defaults, METH_FASTCALL|METH_KEYWORDS, test_vararg_with_only_defaults__doc__}, static PyObject * -test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, +test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, int b, PyObject *c); static PyObject * @@ -3489,16 +3495,17 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize static const char * const _keywords[] = {"b", "c", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_only_defaults", 0}; PyObject *argsbuf[3]; + Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args = NULL; int b = 0; PyObject *c = " "; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; + __clinic_args = (PyObject *const *)args[0]; if (!noptargs) { goto skip_optional_kwonly; } @@ -3513,14 +3520,14 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize } c = args[2]; skip_optional_kwonly: - return_value = test_vararg_with_only_defaults_impl(module, __clinic_args, b, c); + return_value = test_vararg_with_only_defaults_impl(module, varargssize, __clinic_args, b, c); exit: - Py_XDECREF(__clinic_args); return return_value; } static PyObject * -test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b, +test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=dc29ce6ebc2ec10c input=fa56a709a035666e]*/ +/*[clinic end generated code: output=f1415a05ea48fc50 input=fa56a709a035666e]*/ diff --git a/Python/getargs.c b/Python/getargs.c index 3fab3b5efefc28..d9e3f93fbc35ce 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2470,11 +2470,11 @@ _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) + int vararg, Py_ssize_t varargssize, + 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; @@ -2529,26 +2529,12 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, return NULL; } - /* create varargs tuple */ - varargssize = nargs - maxpos; - if (varargssize < 0) { - varargssize = 0; - } - buf[vararg] = PyTuple_New(varargssize); - if (!buf[vararg]) { - return NULL; - } + /* pass varargs by pointer */ + buf[vararg] = (PyObject *)&args[vararg]; - /* 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 { + /* copy required positional args */ + for (i = 0; i < vararg; i++) { buf[i] = args[i]; - } } /* copy keyword args using kwtuple to drive process */ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 4b4ebb8313adca..d5d4fb0049fe4d 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -942,7 +942,7 @@ def parser_body(prototype, *fields, declarations=''): min_kw_only ) else: - args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s" % ( + args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s, varargssize" % ( min_pos, max_pos, min_kw_only, @@ -957,6 +957,8 @@ def parser_body(prototype, *fields, declarations=''): static _PyArg_Parser _parser = {{NULL, _keywords, "{name}", 0}}; PyObject *argsbuf[%s]; """ % len(converters)) + if vararg != NO_VARARG: + declarations += "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) if has_optional_kw: pre_buffer = "0" if vararg != NO_VARARG else "nargs" declarations += "\nPy_ssize_t noptargs = %s + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - %d;" % (pre_buffer, min_pos + min_kw_only) @@ -994,6 +996,8 @@ def parser_body(prototype, *fields, declarations=''): add_label = None for i, p in enumerate(parameters): displayname = p.get_displayname(i+1) + if p.is_vararg(): + p.converter.type = 'PyObject *const *' parsearg = p.converter.parse_arg(argname_fmt % i, displayname) if parsearg is None: #print('Cannot convert %s %r for %s' % (p.converter.__class__.__name__, p.converter.format_unit, p.converter.name), file=sys.stderr) @@ -1305,9 +1309,6 @@ def render_function(self, clinic, f): if (i != -1) and (p.default is not unspecified): first_optional = min(first_optional, i) - if p.is_vararg(): - data.cleanup.append("Py_XDECREF({});".format(c.parser_name)) - # insert group variable group = p.group if last_group != group: @@ -1319,6 +1320,11 @@ def render_function(self, clinic, f): data.impl_parameters.append("int " + group_name) has_option_groups = True + if p.is_vararg(): + p.converter.type = 'PyObject *const *' + data.impl_arguments.append('varargssize') + data.impl_parameters.append('Py_ssize_t varargssize') + c.render(p, data) if has_option_groups and (not positional): From 43b9410d33dfa9ad45a6ad156b649f7551ce5f7a Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Fri, 31 Dec 2021 16:41:10 +0800 Subject: [PATCH 02/22] Optimize argument passing of builtin print() with modified Argument Clinic --- Python/bltinmodule.c | 11 ++++++----- Python/clinic/bltinmodule.c.h | 17 +++++++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 6763f9969707d2..1932343188abcf 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1963,9 +1963,10 @@ Prints the values to a stream, or to sys.stdout by default. [clinic start generated code]*/ static PyObject * -builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, - PyObject *end, PyObject *file, int flush) -/*[clinic end generated code: output=3cfc0940f5bc237b input=c143c575d24fe665]*/ +builtin_print_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, PyObject *sep, PyObject *end, + PyObject *file, int flush) +/*[clinic end generated code: output=50b729b4a8731c69 input=c143c575d24fe665]*/ { int i, err; @@ -2001,7 +2002,7 @@ builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, return NULL; } - for (i = 0; i < PyTuple_GET_SIZE(args); i++) { + for (i = 0; i < varargssize; i++) { if (i > 0) { if (sep == NULL) { err = PyFile_WriteString(" ", file); @@ -2013,7 +2014,7 @@ builtin_print_impl(PyObject *module, PyObject *args, PyObject *sep, return NULL; } } - err = PyFile_WriteObject(PyTuple_GET_ITEM(args, i), file, Py_PRINT_RAW); + err = PyFile_WriteObject(args[i], file, Py_PRINT_RAW); if (err) { return NULL; } diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 1fade994f40e30..8e9b788a1363f7 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -693,8 +693,9 @@ PyDoc_STRVAR(builtin_print__doc__, {"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); +builtin_print_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, PyObject *sep, PyObject *end, + PyObject *file, int flush); static PyObject * builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -703,18 +704,19 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec static const char * const _keywords[] = {"sep", "end", "file", "flush", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "print", 0}; PyObject *argsbuf[5]; + Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *__clinic_args = NULL; + PyObject *const *__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); + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); if (!args) { goto exit; } - __clinic_args = args[0]; + __clinic_args = (PyObject *const *)args[0]; if (!noptargs) { goto skip_optional_kwonly; } @@ -741,10 +743,9 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec goto exit; } skip_optional_kwonly: - return_value = builtin_print_impl(module, __clinic_args, sep, end, file, flush); + return_value = builtin_print_impl(module, varargssize, __clinic_args, sep, end, file, flush); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -951,4 +952,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=77ace832b3fb38e0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f821a31890f729b6 input=a9049054013a1b77]*/ From 9b37a772f3b265eb823e7f884c8f50d68f70941b Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Tue, 4 Jan 2022 15:51:46 +0800 Subject: [PATCH 03/22] Revert modification of PyAPI_FUNC _PyArg_UnpackKeywordsWithVararg to keep backward compatibility --- Include/modsupport.h | 3 +-- Lib/test/clinic.test | 12 ++++++------ Python/clinic/bltinmodule.c.h | 4 ++-- Python/getargs.c | 11 ++++++++--- Tools/clinic/clinic.py | 2 +- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Include/modsupport.h b/Include/modsupport.h index bc84ae4d1c261f..baf47f0038d08d 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -134,8 +134,7 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg( PyObject *kwargs, PyObject *kwnames, struct _PyArg_Parser *parser, int minpos, int maxpos, int minkw, - int vararg, Py_ssize_t varargssize, - PyObject **buf); + int vararg, PyObject **buf); #define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 896ad095073492..12bb1fec188273 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3386,7 +3386,7 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *a; PyObject *const *__clinic_args = NULL; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); if (!args) { goto exit; } @@ -3401,7 +3401,7 @@ exit: static PyObject * test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=9f3d65086405e9b6 input=81d33815ad1bae6e]*/ +/*[clinic end generated code: output=c67c92891ba95cb0 input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -3439,7 +3439,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *const *__clinic_args = NULL; int b = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); if (!args) { goto exit; } @@ -3463,7 +3463,7 @@ static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=711a606d86d09fec input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=a36408b211ae3626 input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -3501,7 +3501,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize int b = 0; PyObject *c = " "; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); if (!args) { goto exit; } @@ -3530,4 +3530,4 @@ static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=f1415a05ea48fc50 input=fa56a709a035666e]*/ +/*[clinic end generated code: output=e626ca46f05eebcc input=fa56a709a035666e]*/ diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 8e9b788a1363f7..a209224acddae6 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -712,7 +712,7 @@ 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, varargssize, argsbuf); + args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); if (!args) { goto exit; } @@ -952,4 +952,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=f821a31890f729b6 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=40c7ad14b0e76975 input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index d9e3f93fbc35ce..9c19af57a1aa2c 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2470,11 +2470,11 @@ _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, Py_ssize_t varargssize, - PyObject **buf) + 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; @@ -2529,12 +2529,17 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, return NULL; } + varargssize = nargs - maxpos; + if (varargssize < 0) { + varargssize = 0; + } + /* pass varargs by pointer */ buf[vararg] = (PyObject *)&args[vararg]; /* copy required positional args */ for (i = 0; i < vararg; i++) { - buf[i] = args[i]; + buf[i] = args[i]; } /* copy keyword args using kwtuple to drive process */ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index d5d4fb0049fe4d..631042da791df5 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -942,7 +942,7 @@ def parser_body(prototype, *fields, declarations=''): min_kw_only ) else: - args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s, varargssize" % ( + args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s" % ( min_pos, max_pos, min_kw_only, From 46e4472961f89def35e3726af4c9f58729f3dd23 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 4 Jan 2022 08:09:32 +0000 Subject: [PATCH 04/22] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst new file mode 100644 index 00000000000000..6a1eaabd0a5bb3 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst @@ -0,0 +1 @@ +Avoid creating temporary varargs tuple in argument passing with "Argument Clinic generated code". \ No newline at end of file From e06f3434f6bdeb1ed8e1aef7f28b75a0b840bf21 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Tue, 4 Jan 2022 18:34:06 +0800 Subject: [PATCH 05/22] Revert _PyArg_UnpackKeywordsWithVararg and add _PyArg_UnpackKeywordsWithVarargFast --- Include/modsupport.h | 8 ++ Lib/test/clinic.test | 12 +-- Python/clinic/bltinmodule.c.h | 4 +- Python/getargs.c | 163 +++++++++++++++++++++++++++++++++- Tools/clinic/clinic.py | 2 +- 5 files changed, 176 insertions(+), 13 deletions(-) diff --git a/Include/modsupport.h b/Include/modsupport.h index baf47f0038d08d..ae7aabb4587642 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -136,6 +136,14 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg( int minpos, int maxpos, int minkw, int vararg, PyObject **buf); +PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVarargFast( + PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + struct _PyArg_Parser *parser, + int minpos, int maxpos, int minkw, + int vararg, Py_ssize_t varargssize, + PyObject **buf); + #define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ (minpos) <= (nargs) && (nargs) <= (maxpos) && args != NULL) ? (args) : \ diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 12bb1fec188273..ad87e478fbef00 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3386,7 +3386,7 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *a; PyObject *const *__clinic_args = NULL; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + args = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); if (!args) { goto exit; } @@ -3401,7 +3401,7 @@ exit: static PyObject * test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=c67c92891ba95cb0 input=81d33815ad1bae6e]*/ +/*[clinic end generated code: output=b9cb9484bd7303db input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -3439,7 +3439,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *const *__clinic_args = NULL; int b = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + args = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); if (!args) { goto exit; } @@ -3463,7 +3463,7 @@ static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=a36408b211ae3626 input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=d38066fd9fa9336f input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -3501,7 +3501,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize int b = 0; PyObject *c = " "; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + args = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); if (!args) { goto exit; } @@ -3530,4 +3530,4 @@ static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=e626ca46f05eebcc input=fa56a709a035666e]*/ +/*[clinic end generated code: output=833c72a533c80a0e input=fa56a709a035666e]*/ diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index a209224acddae6..6d8db17e573827 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -712,7 +712,7 @@ 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); + args = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); if (!args) { goto exit; } @@ -952,4 +952,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=40c7ad14b0e76975 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9582080bde5ff291 input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index 9c19af57a1aa2c..f201dba26f9fc0 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2529,10 +2529,166 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t 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) - + Py_SAFE_DOWNCAST(varargssize, Py_ssize_t, int); 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; +} + +PyObject * const * +_PyArg_UnpackKeywordsWithVarargFast(PyObject *const *args, Py_ssize_t nargs, + PyObject *kwargs, PyObject *kwnames, + struct _PyArg_Parser *parser, + int minpos, int maxpos, int minkw, + int vararg, Py_ssize_t varargssize, + PyObject **buf) +{ + PyObject *kwtuple; + PyObject *keyword; + 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; + } /* pass varargs by pointer */ buf[vararg] = (PyObject *)&args[vararg]; @@ -2544,7 +2700,7 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, /* 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++) { + Py_SAFE_DOWNCAST(varargssize, Py_ssize_t, int); i < maxargs; i++) { if (nkwargs) { keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); if (kwargs != NULL) { @@ -2570,7 +2726,7 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, /* Less arguments than required */ keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); PyErr_Format(PyExc_TypeError, "%.200s%s missing required " - "argument '%U' (pos %d)", + "argument '%U' (pos %d)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", keyword, i+1); @@ -2612,12 +2768,11 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, return buf; -exit: + exit: Py_XDECREF(buf[vararg]); return NULL; } - static const char * skipitem(const char **p_format, va_list *p_va, int flags) { diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 631042da791df5..7285c0e6d00b6a 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -942,7 +942,7 @@ def parser_body(prototype, *fields, declarations=''): min_kw_only ) else: - args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s" % ( + args_declaration = "_PyArg_UnpackKeywordsWithVarargFast", "%s, %s, %s, %s, varargssize" % ( min_pos, max_pos, min_kw_only, From d35410b15349ee55207642bd794988cd412fe6e8 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Tue, 22 Feb 2022 19:54:03 +0800 Subject: [PATCH 06/22] Fix a bug which allows more than one varargs --- Tools/clinic/clinic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 7285c0e6d00b6a..16a8ceabf8ed4c 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -650,7 +650,7 @@ def output_templates(self, f): vararg = NO_VARARG pos_only = min_pos = max_pos = min_kw_only = pseudo_args = 0 for i, p in enumerate(parameters, 1): - if p.is_keyword_only() or vararg != NO_VARARG: + if p.is_keyword_only(): assert not p.is_positional_only() if not p.is_optional(): min_kw_only = i - max_pos From 0a9fe9189238128810936e47257560a96753de85 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Tue, 22 Mar 2022 16:58:27 +0800 Subject: [PATCH 07/22] Do not copy posargs and vararg during argument parsing --- Lib/test/clinic.test | 51 ++++++++++++++------------- Python/clinic/bltinmodule.c.h | 25 ++++++------- Python/getargs.c | 18 ++-------- Tools/clinic/clinic.py | 66 ++++++++++++++++++++--------------- 4 files changed, 79 insertions(+), 81 deletions(-) diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index ad87e478fbef00..16b2cbf70e060b 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3331,17 +3331,15 @@ static PyObject * test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *const *__clinic_args = NULL; + PyObject *const *__clinic_args; if (!_PyArg_CheckPositional("test_vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) { goto exit; } a = args[0]; - __clinic_args = PyTuple_New(nargs - 1); - for (Py_ssize_t i = 0; i < nargs - 1; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, args[1 + i]); - } + __clinic_args = args + 1; return_value = test_vararg_and_posonly_impl(module, a, varargssize, __clinic_args); exit: @@ -3351,7 +3349,7 @@ exit: static PyObject * test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=6a6c69c6ff7deb9b input=08dc2bf7afbf1613]*/ +/*[clinic end generated code: output=23a9d5c5c3cbc015 input=08dc2bf7afbf1613]*/ /*[clinic input] test_vararg @@ -3382,16 +3380,17 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg", 0}; PyObject *argsbuf[2]; Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; - PyObject *const *__clinic_args = NULL; + PyObject *const *__clinic_args; - args = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; - __clinic_args = (PyObject *const *)args[1]; + __clinic_args = args + 1; return_value = test_vararg_impl(module, a, varargssize, __clinic_args); exit: @@ -3401,7 +3400,7 @@ exit: static PyObject * test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=b9cb9484bd7303db input=81d33815ad1bae6e]*/ +/*[clinic end generated code: output=67d632b0b77d2c2b input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -3434,21 +3433,22 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_default", 0}; PyObject *argsbuf[3]; Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; - PyObject *const *__clinic_args = NULL; + PyObject *const *__clinic_args; int b = 0; - args = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; - __clinic_args = (PyObject *const *)args[1]; + __clinic_args = args + 1; if (!noptargs) { goto skip_optional_kwonly; } - b = PyObject_IsTrue(args[2]); + b = PyObject_IsTrue(fastargs[2]); if (b < 0) { goto exit; } @@ -3463,7 +3463,7 @@ static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=d38066fd9fa9336f input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=51af6550b692f8d9 input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -3496,21 +3496,22 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_only_defaults", 0}; PyObject *argsbuf[3]; Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); + PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *const *__clinic_args = NULL; + PyObject *const *__clinic_args; int b = 0; PyObject *c = " "; - args = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = (PyObject *const *)args[0]; + __clinic_args = args + 0; if (!noptargs) { goto skip_optional_kwonly; } - if (args[1]) { - b = PyObject_IsTrue(args[1]); + if (fastargs[1]) { + b = PyObject_IsTrue(fastargs[1]); if (b < 0) { goto exit; } @@ -3518,7 +3519,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize goto skip_optional_kwonly; } } - c = args[2]; + c = fastargs[2]; skip_optional_kwonly: return_value = test_vararg_with_only_defaults_impl(module, varargssize, __clinic_args, b, c); @@ -3530,4 +3531,4 @@ static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=833c72a533c80a0e input=fa56a709a035666e]*/ +/*[clinic end generated code: output=b2a08b5cd2854742 input=fa56a709a035666e]*/ diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 6d8db17e573827..bc0a937f091ee2 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -705,40 +705,41 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec static _PyArg_Parser _parser = {NULL, _keywords, "print", 0}; PyObject *argsbuf[5]; Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); + PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *const *__clinic_args = NULL; + PyObject *const *__clinic_args; PyObject *sep = Py_None; PyObject *end = Py_None; PyObject *file = Py_None; int flush = 0; - args = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = (PyObject *const *)args[0]; + __clinic_args = args + 0; if (!noptargs) { goto skip_optional_kwonly; } - if (args[1]) { - sep = args[1]; + if (fastargs[1]) { + sep = fastargs[1]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (args[2]) { - end = args[2]; + if (fastargs[2]) { + end = fastargs[2]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (args[3]) { - file = args[3]; + if (fastargs[3]) { + file = fastargs[3]; if (!--noptargs) { goto skip_optional_kwonly; } } - flush = PyObject_IsTrue(args[4]); + flush = PyObject_IsTrue(fastargs[4]); if (flush < 0) { goto exit; } @@ -952,4 +953,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=9582080bde5ff291 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=81ac43e8840c5a22 input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index f201dba26f9fc0..7a6da233d6c319 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2690,14 +2690,6 @@ _PyArg_UnpackKeywordsWithVarargFast(PyObject *const *args, Py_ssize_t nargs, return NULL; } - /* pass varargs by pointer */ - buf[vararg] = (PyObject *)&args[vararg]; - - /* copy required positional args */ - for (i = 0; i < vararg; i++) { - 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++) { @@ -2706,7 +2698,7 @@ _PyArg_UnpackKeywordsWithVarargFast(PyObject *const *args, Py_ssize_t nargs, if (kwargs != NULL) { current_arg = PyDict_GetItemWithError(kwargs, keyword); if (!current_arg && PyErr_Occurred()) { - goto exit; + return NULL; } } else { @@ -2730,7 +2722,7 @@ _PyArg_UnpackKeywordsWithVarargFast(PyObject *const *args, Py_ssize_t nargs, (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", keyword, i+1); - goto exit; + return NULL; } } @@ -2761,16 +2753,12 @@ _PyArg_UnpackKeywordsWithVarargFast(PyObject *const *args, Py_ssize_t nargs, (parser->fname == NULL) ? "this function" : parser->fname, (parser->fname == NULL) ? "" : "()"); } - goto exit; + return NULL; } } } return buf; - - exit: - Py_XDECREF(buf[vararg]); - return NULL; } static const char * diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 16a8ceabf8ed4c..2a11b93bbab692 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -862,6 +862,10 @@ def parser_body(prototype, *fields, declarations=''): nargs = 'PyTuple_GET_SIZE(args)' argname_fmt = 'PyTuple_GET_ITEM(args, %d)' + if vararg != NO_VARARG: + declarations = "Py_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) + else: + declarations = "" left_args = "{} - {}".format(nargs, max_pos) max_args = NO_VARARG if (vararg != NO_VARARG) else max_pos @@ -879,24 +883,17 @@ def parser_body(prototype, *fields, declarations=''): if p.is_vararg(): if not new_or_init: parser_code.append(normalize_snippet(""" - %s = PyTuple_New(%s); - for (Py_ssize_t i = 0; i < %s; ++i) {{ - PyTuple_SET_ITEM(%s, i, args[%d + i]); - }} + %s = %s + %d; """ % ( - p.converter.parser_name, - left_args, - left_args, - p.converter.parser_name, - max_pos - ), indent=4)) + p.converter.parser_name, + p.name, + vararg + ), indent=4)) else: + # return a borrowed reference of items in args tuple parser_code.append(normalize_snippet(""" - %s = PyTuple_GetSlice(%d, -1); - """ % ( - p.converter.parser_name, - max_pos - ), indent=4)) + %s = _PyTuple_CAST(args)->ob_item; + """ % p.converter.parser_name, indent=4)) continue parsearg = p.converter.parse_arg(argname, displayname) @@ -931,7 +928,7 @@ def parser_body(prototype, *fields, declarations=''): goto exit; }} """, indent=4)] - parser_definition = parser_body(parser_prototype, *parser_code) + parser_definition = parser_body(parser_prototype, *parser_code, declarations=declarations) else: has_optional_kw = (max(pos_only, min_pos) + min_kw_only < len(converters)) @@ -951,7 +948,6 @@ def parser_body(prototype, *fields, declarations=''): if not new_or_init: flags = "METH_FASTCALL|METH_KEYWORDS" parser_prototype = parser_prototype_fastcall_keywords - argname_fmt = 'args[%d]' declarations = normalize_snippet(""" static const char * const _keywords[] = {{{keywords} NULL}}; static _PyArg_Parser _parser = {{NULL, _keywords, "{name}", 0}}; @@ -959,15 +955,20 @@ def parser_body(prototype, *fields, declarations=''): """ % len(converters)) if vararg != NO_VARARG: declarations += "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) + parsed_argname = "fastargs" + declarations += "\nPyObject *const *fastargs;" + else: + parsed_argname = "args" if has_optional_kw: pre_buffer = "0" if vararg != NO_VARARG else "nargs" declarations += "\nPy_ssize_t noptargs = %s + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - %d;" % (pre_buffer, min_pos + min_kw_only) - parser_code = [normalize_snippet(""" - args = %s(args, nargs, NULL, kwnames, &_parser, %s, argsbuf); - if (!args) {{ + parser_code = [normalize_snippet(f""" + {parsed_argname} = %s(args, nargs, NULL, kwnames, &_parser, %s, argsbuf); + if (!{parsed_argname}) {{{{ goto exit; - }} + }}}} """ % args_declaration, indent=4)] + argname_fmt = f"{parsed_argname}[%d]" else: # positional-or-keyword arguments flags = "METH_VARARGS|METH_KEYWORDS" @@ -980,6 +981,8 @@ def parser_body(prototype, *fields, declarations=''): PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); """ % len(converters)) + if vararg != NO_VARARG: + declarations += "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) if has_optional_kw: declarations += "\nPy_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - %d;" % (min_pos + min_kw_only) parser_code = [normalize_snippet(""" @@ -996,9 +999,18 @@ def parser_body(prototype, *fields, declarations=''): add_label = None for i, p in enumerate(parameters): displayname = p.get_displayname(i+1) - if p.is_vararg(): - p.converter.type = 'PyObject *const *' - parsearg = p.converter.parse_arg(argname_fmt % i, displayname) + if vararg != NO_VARARG: + # positional args + if i < int(vararg): + parsearg = p.converter.parse_arg('args[%d]' % i, displayname) + # keyword args + elif i > int(vararg): + parsearg = p.converter.parse_arg('fastargs[%d]' % i, displayname) + # vararg + else: + parsearg = p.converter.parse_arg('args + %d' % vararg, p.converter.parser_name) + else: + parsearg = p.converter.parse_arg(argname_fmt % i, displayname) if parsearg is None: #print('Cannot convert %s %r for %s' % (p.converter.__class__.__name__, p.converter.format_unit, p.converter.name), file=sys.stderr) parser_code = None @@ -4534,11 +4546,7 @@ def state_parameter(self, line): if not default: if self.parameter_state == self.ps_optional: fail("Can't have a parameter without a default (" + repr(parameter_name) + ")\nafter a parameter with a default!") - if is_vararg: - value = NULL - kwargs.setdefault('c_default', "NULL") - else: - value = unspecified + value = unspecified if 'py_default' in kwargs: fail("You can't specify py_default without specifying a default value!") else: From 9188052d6ecce373dcf32d826db1a018517f9578 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Wed, 23 Mar 2022 16:30:43 +0800 Subject: [PATCH 08/22] Rename _PyArg_UnpackKeywordsWithVarargFast, it returns kwargs only now --- Include/modsupport.h | 5 ++--- Lib/test/clinic.test | 26 +++++++++++++------------- Python/clinic/bltinmodule.c.h | 20 ++++++++++---------- Python/getargs.c | 9 +++++---- Tools/clinic/clinic.py | 14 ++++++++------ 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/Include/modsupport.h b/Include/modsupport.h index ae7aabb4587642..247ae796b0f4f0 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -136,13 +136,12 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg( int minpos, int maxpos, int minkw, int vararg, PyObject **buf); -PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVarargFast( +PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVarargKwonly( PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, PyObject *kwnames, struct _PyArg_Parser *parser, int minpos, int maxpos, int minkw, - int vararg, Py_ssize_t varargssize, - PyObject **buf); + int vararg, PyObject **buf); #define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 16b2cbf70e060b..f89f3f38376e6a 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3378,14 +3378,14 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *return_value = NULL; static const char * const _keywords[] = {"a", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg", 0}; - PyObject *argsbuf[2]; + PyObject *argsbuf[0]; Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; PyObject *const *__clinic_args; - fastargs = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); if (!fastargs) { goto exit; } @@ -3400,7 +3400,7 @@ exit: static PyObject * test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=67d632b0b77d2c2b input=81d33815ad1bae6e]*/ +/*[clinic end generated code: output=c200ba15688e2a36 input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -3431,7 +3431,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *return_value = NULL; static const char * const _keywords[] = {"a", "b", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_default", 0}; - PyObject *argsbuf[3]; + PyObject *argsbuf[1]; Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; @@ -3439,7 +3439,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *const *__clinic_args; int b = 0; - fastargs = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, varargssize, argsbuf); + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); if (!fastargs) { goto exit; } @@ -3448,7 +3448,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar if (!noptargs) { goto skip_optional_kwonly; } - b = PyObject_IsTrue(fastargs[2]); + b = PyObject_IsTrue(fastargs[0]); if (b < 0) { goto exit; } @@ -3463,7 +3463,7 @@ static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=51af6550b692f8d9 input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=880cc2e95cfff4dd input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -3494,7 +3494,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize PyObject *return_value = NULL; static const char * const _keywords[] = {"b", "c", NULL}; static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_only_defaults", 0}; - PyObject *argsbuf[3]; + PyObject *argsbuf[2]; Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; @@ -3502,7 +3502,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize int b = 0; PyObject *c = " "; - fastargs = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); if (!fastargs) { goto exit; } @@ -3510,8 +3510,8 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize if (!noptargs) { goto skip_optional_kwonly; } - if (fastargs[1]) { - b = PyObject_IsTrue(fastargs[1]); + if (fastargs[0]) { + b = PyObject_IsTrue(fastargs[0]); if (b < 0) { goto exit; } @@ -3519,7 +3519,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize goto skip_optional_kwonly; } } - c = fastargs[2]; + c = fastargs[1]; skip_optional_kwonly: return_value = test_vararg_with_only_defaults_impl(module, varargssize, __clinic_args, b, c); @@ -3531,4 +3531,4 @@ static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=b2a08b5cd2854742 input=fa56a709a035666e]*/ +/*[clinic end generated code: output=99d5e997bbcdf47a input=fa56a709a035666e]*/ diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index bc0a937f091ee2..bb23bca9c8663e 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -703,7 +703,7 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec 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]; + PyObject *argsbuf[4]; Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; @@ -713,7 +713,7 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *file = Py_None; int flush = 0; - fastargs = _PyArg_UnpackKeywordsWithVarargFast(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, varargssize, argsbuf); + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); if (!fastargs) { goto exit; } @@ -721,25 +721,25 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec if (!noptargs) { goto skip_optional_kwonly; } - if (fastargs[1]) { - sep = fastargs[1]; + if (fastargs[0]) { + sep = fastargs[0]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (fastargs[2]) { - end = fastargs[2]; + if (fastargs[1]) { + end = fastargs[1]; if (!--noptargs) { goto skip_optional_kwonly; } } - if (fastargs[3]) { - file = fastargs[3]; + if (fastargs[2]) { + file = fastargs[2]; if (!--noptargs) { goto skip_optional_kwonly; } } - flush = PyObject_IsTrue(fastargs[4]); + flush = PyObject_IsTrue(fastargs[3]); if (flush < 0) { goto exit; } @@ -953,4 +953,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=81ac43e8840c5a22 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8e18b575dd06dbb8 input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index 7a6da233d6c319..32f519557ceb38 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2627,15 +2627,15 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, } PyObject * const * -_PyArg_UnpackKeywordsWithVarargFast(PyObject *const *args, Py_ssize_t nargs, +_PyArg_UnpackKeywordsWithVarargKwonly(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, PyObject *kwnames, struct _PyArg_Parser *parser, int minpos, int maxpos, int minkw, - int vararg, Py_ssize_t varargssize, - PyObject **buf) + int vararg, PyObject **buf) { PyObject *kwtuple; PyObject *keyword; + Py_ssize_t varargssize = Py_MAX(nargs - maxpos, 0); int i, posonly, minposonly, maxargs; int reqlimit = minkw ? maxpos + minkw : minpos; Py_ssize_t nkwargs; @@ -2667,6 +2667,7 @@ _PyArg_UnpackKeywordsWithVarargFast(PyObject *const *args, Py_ssize_t nargs, posonly = parser->pos; minposonly = Py_MIN(posonly, minpos); maxargs = posonly + (int)PyTuple_GET_SIZE(kwtuple); + if (kwargs != NULL) { nkwargs = PyDict_GET_SIZE(kwargs); } @@ -2709,7 +2710,7 @@ _PyArg_UnpackKeywordsWithVarargFast(PyObject *const *args, Py_ssize_t nargs, current_arg = NULL; } - buf[i + vararg + 1] = current_arg; + buf[i - vararg] = current_arg; if (current_arg) { --nkwargs; diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 2a11b93bbab692..4e58c3e0411597 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -938,13 +938,15 @@ def parser_body(prototype, *fields, declarations=''): max_pos, min_kw_only ) + argsbuf_size = len(converters) else: - args_declaration = "_PyArg_UnpackKeywordsWithVarargFast", "%s, %s, %s, %s, varargssize" % ( + args_declaration = "_PyArg_UnpackKeywordsWithVarargKwonly", "%s, %s, %s, %s" % ( min_pos, max_pos, min_kw_only, vararg ) + argsbuf_size = len(converters) - vararg - 1 if not new_or_init: flags = "METH_FASTCALL|METH_KEYWORDS" parser_prototype = parser_prototype_fastcall_keywords @@ -952,7 +954,7 @@ def parser_body(prototype, *fields, declarations=''): static const char * const _keywords[] = {{{keywords} NULL}}; static _PyArg_Parser _parser = {{NULL, _keywords, "{name}", 0}}; PyObject *argsbuf[%s]; - """ % len(converters)) + """ % argsbuf_size) if vararg != NO_VARARG: declarations += "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) parsed_argname = "fastargs" @@ -980,7 +982,7 @@ def parser_body(prototype, *fields, declarations=''): PyObject *argsbuf[%s]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); - """ % len(converters)) + """ % argsbuf_size) if vararg != NO_VARARG: declarations += "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) if has_optional_kw: @@ -998,14 +1000,14 @@ def parser_body(prototype, *fields, declarations=''): add_label = None for i, p in enumerate(parameters): - displayname = p.get_displayname(i+1) + displayname = p.get_displayname(i + 1) if vararg != NO_VARARG: # positional args if i < int(vararg): parsearg = p.converter.parse_arg('args[%d]' % i, displayname) # keyword args elif i > int(vararg): - parsearg = p.converter.parse_arg('fastargs[%d]' % i, displayname) + parsearg = p.converter.parse_arg('fastargs[%d]' % (i - vararg - 1), displayname) # vararg else: parsearg = p.converter.parse_arg('args + %d' % vararg, p.converter.parser_name) @@ -1054,7 +1056,7 @@ def parser_body(prototype, *fields, declarations=''): add_label = label parser_code.append(normalize_snippet(""" if (%s) {{ - """ % (argname_fmt % i), indent=4)) + """ % (argname_fmt % (i if vararg == NO_VARARG else i - vararg - 1)), indent=4)) parser_code.append(normalize_snippet(parsearg, indent=8)) parser_code.append(normalize_snippet(""" if (!--noptargs) {{ From d245ec01ebf13a2e36baa67f5401f6669f3a36b3 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Wed, 23 Mar 2022 16:32:06 +0800 Subject: [PATCH 09/22] Check type of varargs when generating argument parser --- Tools/clinic/clinic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 4e58c3e0411597..1b93d236b5426e 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -655,6 +655,8 @@ def output_templates(self, f): if not p.is_optional(): min_kw_only = i - max_pos elif p.is_vararg(): + if not isinstance(p.converter, object_converter): + fail("Only object type varargs are accepted") if vararg != NO_VARARG: fail("Too many var args") pseudo_args += 1 From 8d1668531857b1cb72829683b6c1e5819d1e2aa6 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Wed, 23 Mar 2022 17:16:57 +0800 Subject: [PATCH 10/22] Fix varargssize calculation in class init and add test cases --- Lib/test/clinic.test | 102 +++++++++++++++++++++++++++++++++++++++++ Tools/clinic/clinic.py | 2 +- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index f89f3f38376e6a..8b29f43a0a7e21 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3532,3 +3532,105 @@ test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, PyObject *const *args, int b, PyObject *c) /*[clinic end generated code: output=99d5e997bbcdf47a input=fa56a709a035666e]*/ + + +/*[clinic input] +module TestModule +class TestModule.TestClass1 "TestClassObject *" "&TestModule_Type" +class TestModule.TestClass2 "TestClassObject *" "&TestModule_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=cd38925654911fd4]*/ + + +/*[clinic input] +TestModule.TestClass1.__init__ + + *args: object + +[clinic start generated code]*/ + +static int +TestModule_TestClass1___init___impl(TestClassObject *self, + Py_ssize_t varargssize, + PyObject *const *args); + +static int +TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + Py_ssize_t varargssize = Py_MAX(PyTuple_GET_SIZE(args) - 0, 0); + PyObject *const *__clinic_args; + + if ((Py_IS_TYPE(self, &TestModule_Type) || + Py_TYPE(self)->tp_new == TestModule_Type.tp_new) && + !_PyArg_NoKeywords("TestClass1", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("TestClass1", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = _PyTuple_CAST(args)->ob_item; + return_value = TestModule_TestClass1___init___impl((TestClassObject *)self, varargssize, __clinic_args); + +exit: + return return_value; +} + +static int +TestModule_TestClass1___init___impl(TestClassObject *self, + Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=6cc55e8fae8d8bf2 input=3e093b8ea0b74b0c]*/ + + +/*[clinic input] +TestModule.TestClass1.__init__ + + pos: object + *args: object + kw: object = None + +[clinic start generated code]*/ + +static int +TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, + Py_ssize_t varargssize, + PyObject *const *args, PyObject *kw); + +static int +TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + static const char * const _keywords[] = {"pos", "kw", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "TestClass1", 0}; + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + PyObject *pos; + PyObject *const *__clinic_args; + PyObject *kw = Py_None; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + pos = args[0]; + __clinic_args = args + 1; + if (!noptargs) { + goto skip_optional_kwonly; + } + kw = fastargs[0]; +skip_optional_kwonly: + return_value = TestModule_TestClass1___init___impl((TestClassObject *)self, pos, varargssize, __clinic_args, kw); + +exit: + return return_value; +} + +static int +TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, + Py_ssize_t varargssize, + PyObject *const *args, PyObject *kw) +/*[clinic end generated code: output=fd92077792f4c3ef input=da8e0daa18983316]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 1b93d236b5426e..59ac86b2ad31da 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -865,7 +865,7 @@ def parser_body(prototype, *fields, declarations=''): argname_fmt = 'PyTuple_GET_ITEM(args, %d)' if vararg != NO_VARARG: - declarations = "Py_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) + declarations = "Py_ssize_t varargssize = Py_MAX(%s - %d, 0);" % (nargs, max_pos) else: declarations = "" From 156f8d69e51838ce3e4034570277d5cc994c78a9 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Wed, 23 Mar 2022 17:24:24 +0800 Subject: [PATCH 11/22] Rerun make clinic --- Python/clinic/bltinmodule.c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index a01ae1e69915f7..a936d81aefc64a 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -1032,4 +1032,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=d341fa7525f30070 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f743bba2a1a700c6 input=a9049054013a1b77]*/ From c32aed4b4922ada073f258e8f0d8c7b3f42ba9ad Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Wed, 23 Mar 2022 20:14:59 +0800 Subject: [PATCH 12/22] Edit documentation --- .../2022-01-04-08-09-32.bpo-46212._Fvxj6.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst index 6a1eaabd0a5bb3..f92c906f5abd5d 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst @@ -1 +1,4 @@ -Avoid creating temporary varargs tuple in argument passing with "Argument Clinic generated code". \ No newline at end of file +Avoid creating temporary varargs tuple in argument passing with "Argument Clinic generated code". + +Introduce a function `_PyArg_UnpackKeywordsWithVarargKwonly`, which parse positional args, varargs and keyword args, +but returns an array of ordered keyword args only. From 3ac2821b0d4923af3f3cb20d5eb2780841dc2f97 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Fri, 3 Feb 2023 15:01:11 +0800 Subject: [PATCH 13/22] Fix errors introduced by merging and rerun make clinic --- Include/cpython/modsupport.h | 7 ++ Lib/test/clinic.test | 78 +++++++++++------ Modules/_testclinic.c | 93 +++++++++++++++----- Modules/clinic/_testclinic.c.h | 155 +++++++++++++++++---------------- Python/clinic/bltinmodule.c.h | 2 +- Python/getargs.c | 48 ++++------ Tools/clinic/clinic.py | 2 +- 7 files changed, 226 insertions(+), 159 deletions(-) diff --git a/Include/cpython/modsupport.h b/Include/cpython/modsupport.h index 88f34fe7513bf2..3afec5a3f62c1f 100644 --- a/Include/cpython/modsupport.h +++ b/Include/cpython/modsupport.h @@ -99,6 +99,13 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg( int minpos, int maxpos, int minkw, int vararg, PyObject **buf); +PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVarargKwonly( + 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); + #define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \ (((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \ (minpos) <= (nargs) && (nargs) <= (maxpos) && (args) != NULL) ? (args) : \ diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 5b9b46f724f814..01c2a55458022a 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3792,7 +3792,7 @@ exit: static PyObject * test_vararg_and_posonly_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=23a9d5c5c3cbc015 input=08dc2bf7afbf1613]*/ +/*[clinic end generated code: output=0c242df582a012d1 input=08dc2bf7afbf1613]*/ /*[clinic input] test_vararg @@ -3847,7 +3847,6 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject PyObject *argsbuf[0]; Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; - Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; PyObject *const *__clinic_args; @@ -3866,7 +3865,7 @@ exit: static PyObject * test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=c200ba15688e2a36 input=81d33815ad1bae6e]*/ +/*[clinic end generated code: output=fb100afa02682813 input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -3923,7 +3922,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar PyObject *argsbuf[1]; Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; - Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; PyObject *const *__clinic_args; int b = 0; @@ -3952,7 +3951,7 @@ static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=880cc2e95cfff4dd input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=c724b7a1b327f798 input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -4043,7 +4042,7 @@ static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=dd21b28f0db26a4b input=fa56a709a035666e]*/ +/*[clinic end generated code: output=bdc061cc11a3abde input=fa56a709a035666e]*/ /*[clinic input] test_paramname_module @@ -4125,20 +4124,20 @@ TestModule.TestClass1.__init__ [clinic start generated code]*/ - static int - TestModule_TestClass1___init___impl(TestClassObject *self, - Py_ssize_t varargssize, - PyObject *const *args); +static int +TestModule_TestClass1___init___impl(TestClassObject *self, + Py_ssize_t varargssize, + PyObject *const *args); - static int - TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) +static int +TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; - Py_ssize_t varargssize = Py_MAX(PyTuple_GET_SIZE(args) - 0, 0); + PyTypeObject *base_tp = &TestModule_Type; PyObject *const *__clinic_args; - if ((Py_IS_TYPE(self, &TestModule_Type) || - Py_TYPE(self)->tp_new == TestModule_Type.tp_new) && + if ((Py_IS_TYPE(self, base_tp) || + Py_TYPE(self)->tp_new == base_tp->tp_new) && !_PyArg_NoKeywords("TestClass1", kwargs)) { goto exit; } @@ -4148,7 +4147,7 @@ TestModule.TestClass1.__init__ __clinic_args = _PyTuple_CAST(args)->ob_item; return_value = TestModule_TestClass1___init___impl((TestClassObject *)self, varargssize, __clinic_args); - exit: +exit: return return_value; } @@ -4156,7 +4155,7 @@ static int TestModule_TestClass1___init___impl(TestClassObject *self, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=6cc55e8fae8d8bf2 input=3e093b8ea0b74b0c]*/ +/*[clinic end generated code: output=89fc8cfb766b0097 input=3e093b8ea0b74b0c]*/ /*[clinic input] @@ -4168,22 +4167,45 @@ TestModule.TestClass1.__init__ [clinic start generated code]*/ - static int - TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, - Py_ssize_t varargssize, - PyObject *const *args, PyObject *kw); +static int +TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, + Py_ssize_t varargssize, + PyObject *const *args, PyObject *kw); - static int - TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) +static int +TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(pos), &_Py_ID(kw), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + static const char * const _keywords[] = {"pos", "kw", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "TestClass1", 0}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "TestClass1", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE PyObject *argsbuf[1]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); - Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; PyObject *pos; PyObject *const *__clinic_args; PyObject *kw = Py_None; @@ -4198,10 +4220,10 @@ TestModule.TestClass1.__init__ goto skip_optional_kwonly; } kw = fastargs[0]; - skip_optional_kwonly: +skip_optional_kwonly: return_value = TestModule_TestClass1___init___impl((TestClassObject *)self, pos, varargssize, __clinic_args, kw); - exit: +exit: return return_value; } @@ -4209,4 +4231,4 @@ static int TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, Py_ssize_t varargssize, PyObject *const *args, PyObject *kw) -/*[clinic end generated code: output=fd92077792f4c3ef input=da8e0daa18983316]*/ +/*[clinic end generated code: output=b7e8fba5e3e6e531 input=da8e0daa18983316]*/ diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 91fdee24d328d9..adab7d0db5b356 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -12,6 +12,20 @@ #include "clinic/_testclinic.c.h" +static PyObject * +pack_varargs_to_tuple(Py_ssize_t varargssize, PyObject *const *args) +{ + assert(!PyErr_Occurred()); + PyObject *tuple = PyTuple_New(varargssize); + if (!tuple) { + return NULL; + } + for (Py_ssize_t i = 0; i < varargssize; i++) { + PyTuple_SET_ITEM(tuple, i, Py_NewRef(args[i])); + } + return tuple; +} + /* Pack arguments to a tuple, implicitly increase all the arguments' refcount. * NULL arguments will be replaced to Py_None. */ static PyObject * @@ -962,10 +976,16 @@ posonly_vararg static PyObject * posonly_vararg_impl(PyObject *module, PyObject *a, PyObject *b, - PyObject *args) -/*[clinic end generated code: output=ee6713acda6b954e input=783427fe7ec2b67a]*/ + Py_ssize_t varargssize, PyObject *const *args) +/*[clinic end generated code: output=744bd14cfaa7b46c input=783427fe7ec2b67a]*/ { - return pack_arguments_newref(3, a, b, args); + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(3, a, b, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -979,10 +999,17 @@ vararg_and_posonly [clinic start generated code]*/ static PyObject * -vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=42792f799465a14d input=defe017b19ba52e8]*/ +vararg_and_posonly_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args) +/*[clinic end generated code: output=75f9bcbfc99efb90 input=defe017b19ba52e8]*/ { - return pack_arguments_newref(2, a, args); + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(2, a, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -995,10 +1022,17 @@ vararg [clinic start generated code]*/ static PyObject * -vararg_impl(PyObject *module, PyObject *a, PyObject *args) -/*[clinic end generated code: output=91ab7a0efc52dd5e input=02c0f772d05f591e]*/ +vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=36e183ea6ecb9f8f input=02c0f772d05f591e]*/ { - return pack_arguments_newref(2, a, args); + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(2, a, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; } @@ -1012,12 +1046,19 @@ vararg_with_default [clinic start generated code]*/ static PyObject * -vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, +vararg_with_default_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=182c01035958ce92 input=68cafa6a79f89e36]*/ +/*[clinic end generated code: output=f4e18bc71825f3f9 input=68cafa6a79f89e36]*/ { + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } PyObject *obj_b = b ? Py_True : Py_False; - return pack_arguments_newref(3, a, args, obj_b); + PyObject *result = pack_arguments_newref(3, a, vararg_tuple, obj_b); + Py_DECREF(vararg_tuple); + return result; } @@ -1030,10 +1071,17 @@ vararg_with_only_defaults [clinic start generated code]*/ static PyObject * -vararg_with_only_defaults_impl(PyObject *module, PyObject *args, PyObject *b) -/*[clinic end generated code: output=c06b1826d91f2f7b input=678c069bc67550e1]*/ +vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, PyObject *b) +/*[clinic end generated code: output=049328b782106d03 input=678c069bc67550e1]*/ { - return pack_arguments_newref(2, args, b); + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(2, vararg_tuple, b); + Py_DECREF(vararg_tuple); + return result; } @@ -1053,8 +1101,9 @@ Proof-of-concept of GH-32092 OOB bug. static PyObject * gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2, - PyObject *varargs, PyObject *kw1, PyObject *kw2) -/*[clinic end generated code: output=ee259c130054653f input=46d15c881608f8ff]*/ + Py_ssize_t varargssize, PyObject *const *varargs, + PyObject *kw1, PyObject *kw2) +/*[clinic end generated code: output=ecb8573caf9791ac input=46d15c881608f8ff]*/ { Py_RETURN_NONE; } @@ -1072,9 +1121,10 @@ Proof-of-concept of GH-32092 keyword args passing bug. [clinic start generated code]*/ static PyObject * -gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args, +gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, + Py_ssize_t varargssize, PyObject *const *args, PyObject *kw) -/*[clinic end generated code: output=4a2bbe4f7c8604e9 input=5c0bd5b9079a0cce]*/ +/*[clinic end generated code: output=5fb8caec2618b065 input=5c0bd5b9079a0cce]*/ { Py_RETURN_NONE; } @@ -1091,8 +1141,9 @@ Proof-of-concept of GH-99233 refcount error bug. [clinic start generated code]*/ static PyObject * -gh_99233_refcount_impl(PyObject *module, PyObject *args) -/*[clinic end generated code: output=585855abfbca9a7f input=85f5fb47ac91a626]*/ +gh_99233_refcount_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=2712f6e3397e4eca input=85f5fb47ac91a626]*/ { Py_RETURN_NONE; } diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 831f58ca650aab..7d369cecafe9bb 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2336,7 +2336,7 @@ PyDoc_STRVAR(posonly_vararg__doc__, static PyObject * posonly_vararg_impl(PyObject *module, PyObject *a, PyObject *b, - PyObject *args); + Py_ssize_t varargssize, PyObject *const *args); static PyObject * posonly_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2367,22 +2367,23 @@ posonly_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[0]; + Py_ssize_t varargssize = Py_MAX(nargs - 2, 0); + PyObject *const *fastargs; PyObject *a; PyObject *b; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; b = args[1]; - __clinic_args = args[2]; - return_value = posonly_vararg_impl(module, a, b, __clinic_args); + __clinic_args = args + 2; + return_value = posonly_vararg_impl(module, a, b, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2395,30 +2396,25 @@ PyDoc_STRVAR(vararg_and_posonly__doc__, {"vararg_and_posonly", _PyCFunction_CAST(vararg_and_posonly), METH_FASTCALL, vararg_and_posonly__doc__}, static PyObject * -vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args); +vararg_and_posonly_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args); static PyObject * vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; if (!_PyArg_CheckPositional("vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) { goto exit; } a = args[0]; - __clinic_args = PyTuple_New(nargs - 1); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 1; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[1 + i])); - } - return_value = vararg_and_posonly_impl(module, a, __clinic_args); + __clinic_args = args + 1; + return_value = vararg_and_posonly_impl(module, a, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2431,7 +2427,8 @@ PyDoc_STRVAR(vararg__doc__, {"vararg", _PyCFunction_CAST(vararg), METH_FASTCALL|METH_KEYWORDS, vararg__doc__}, static PyObject * -vararg_impl(PyObject *module, PyObject *a, PyObject *args); +vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, + PyObject *const *args); static PyObject * vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2462,20 +2459,21 @@ vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwna .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[0]; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *const *fastargs; PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; - __clinic_args = args[1]; - return_value = vararg_impl(module, a, __clinic_args); + __clinic_args = args + 1; + return_value = vararg_impl(module, a, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2488,7 +2486,8 @@ PyDoc_STRVAR(vararg_with_default__doc__, {"vararg_with_default", _PyCFunction_CAST(vararg_with_default), METH_FASTCALL|METH_KEYWORDS, vararg_with_default__doc__}, static PyObject * -vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args, +vararg_with_default_impl(PyObject *module, PyObject *a, + Py_ssize_t varargssize, PyObject *const *args, int b); static PyObject * @@ -2520,30 +2519,31 @@ vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[1]; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *a; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; int b = 0; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { goto exit; } a = args[0]; - __clinic_args = args[1]; + __clinic_args = args + 1; if (!noptargs) { goto skip_optional_kwonly; } - b = PyObject_IsTrue(args[2]); + b = PyObject_IsTrue(fastargs[0]); if (b < 0) { goto exit; } skip_optional_kwonly: - return_value = vararg_with_default_impl(module, a, __clinic_args, b); + return_value = vararg_with_default_impl(module, a, varargssize, __clinic_args, b); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2556,7 +2556,8 @@ PyDoc_STRVAR(vararg_with_only_defaults__doc__, {"vararg_with_only_defaults", _PyCFunction_CAST(vararg_with_only_defaults), METH_FASTCALL|METH_KEYWORDS, vararg_with_only_defaults__doc__}, static PyObject * -vararg_with_only_defaults_impl(PyObject *module, PyObject *args, PyObject *b); +vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args, PyObject *b); static PyObject * vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2587,25 +2588,26 @@ vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t na .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[2]; + PyObject *argsbuf[1]; + Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); + PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; PyObject *b = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf); + if (!fastargs) { goto exit; } - __clinic_args = args[0]; + __clinic_args = args + 0; if (!noptargs) { goto skip_optional_kwonly; } - b = args[1]; + b = fastargs[0]; skip_optional_kwonly: - return_value = vararg_with_only_defaults_impl(module, __clinic_args, b); + return_value = vararg_with_only_defaults_impl(module, varargssize, __clinic_args, b); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2620,7 +2622,8 @@ PyDoc_STRVAR(gh_32092_oob__doc__, static PyObject * gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2, - PyObject *varargs, PyObject *kw1, PyObject *kw2); + Py_ssize_t varargssize, PyObject *const *varargs, + PyObject *kw1, PyObject *kw2); static PyObject * gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -2651,36 +2654,37 @@ gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[5]; + PyObject *argsbuf[2]; + Py_ssize_t varargssize = Py_MAX(nargs - 2, 0); + PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 2) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; PyObject *pos1; PyObject *pos2; - PyObject *varargs = NULL; + PyObject *const *varargs; PyObject *kw1 = Py_None; PyObject *kw2 = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf); + if (!fastargs) { goto exit; } pos1 = args[0]; pos2 = args[1]; - varargs = args[2]; + varargs = args + 2; if (!noptargs) { goto skip_optional_kwonly; } - if (args[3]) { - kw1 = args[3]; + if (fastargs[0]) { + kw1 = fastargs[0]; if (!--noptargs) { goto skip_optional_kwonly; } } - kw2 = args[4]; + kw2 = fastargs[1]; skip_optional_kwonly: - return_value = gh_32092_oob_impl(module, pos1, pos2, varargs, kw1, kw2); + return_value = gh_32092_oob_impl(module, pos1, pos2, varargssize, varargs, kw1, kw2); exit: - Py_XDECREF(varargs); return return_value; } @@ -2694,7 +2698,8 @@ PyDoc_STRVAR(gh_32092_kw_pass__doc__, {"gh_32092_kw_pass", _PyCFunction_CAST(gh_32092_kw_pass), METH_FASTCALL|METH_KEYWORDS, gh_32092_kw_pass__doc__}, static PyObject * -gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args, +gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, + Py_ssize_t varargssize, PyObject *const *args, PyObject *kw); static PyObject * @@ -2726,27 +2731,28 @@ gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[1]; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; PyObject *pos; - PyObject *__clinic_args = NULL; + PyObject *const *__clinic_args; PyObject *kw = Py_None; - args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); - if (!args) { + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { goto exit; } pos = args[0]; - __clinic_args = args[1]; + __clinic_args = args + 1; if (!noptargs) { goto skip_optional_kwonly; } - kw = args[2]; + kw = fastargs[0]; skip_optional_kwonly: - return_value = gh_32092_kw_pass_impl(module, pos, __clinic_args, kw); + return_value = gh_32092_kw_pass_impl(module, pos, varargssize, __clinic_args, kw); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2760,28 +2766,23 @@ PyDoc_STRVAR(gh_99233_refcount__doc__, {"gh_99233_refcount", _PyCFunction_CAST(gh_99233_refcount), METH_FASTCALL, gh_99233_refcount__doc__}, static PyObject * -gh_99233_refcount_impl(PyObject *module, PyObject *args); +gh_99233_refcount_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *args); static PyObject * gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - PyObject *__clinic_args = NULL; + Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); + PyObject *const *__clinic_args; if (!_PyArg_CheckPositional("gh_99233_refcount", nargs, 0, PY_SSIZE_T_MAX)) { goto exit; } - __clinic_args = PyTuple_New(nargs - 0); - if (!__clinic_args) { - goto exit; - } - for (Py_ssize_t i = 0; i < nargs - 0; ++i) { - PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i])); - } - return_value = gh_99233_refcount_impl(module, __clinic_args); + __clinic_args = args + 0; + return_value = gh_99233_refcount_impl(module, varargssize, __clinic_args); exit: - Py_XDECREF(__clinic_args); return return_value; } @@ -2817,4 +2818,4 @@ gh_99240_double_free(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=e8211606b03d733a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7a5b64c86324db97 input=a9049054013a1b77]*/ diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index d94de6bccdbdf8..6396dc9204f1ce 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -1411,4 +1411,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=84a04e7446debf58 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=86444999d5dd52a7 input=a9049054013a1b77]*/ diff --git a/Python/getargs.c b/Python/getargs.c index a84a91c9b1a6ce..5c3d589a9ff5f2 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2645,6 +2645,8 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, return NULL; } +/* Specialized version of _PyArg_UnpackKeywordsWithVararg. + * This function only returns parsed kwonly arguments array. */ PyObject * const * _PyArg_UnpackKeywordsWithVarargKwonly(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, PyObject *kwnames, @@ -2686,7 +2688,6 @@ _PyArg_UnpackKeywordsWithVarargKwonly(PyObject *const *args, Py_ssize_t nargs, posonly = parser->pos; minposonly = Py_MIN(posonly, minpos); maxargs = posonly + (int)PyTuple_GET_SIZE(kwtuple); - if (kwargs != NULL) { nkwargs = PyDict_GET_SIZE(kwargs); } @@ -2729,7 +2730,19 @@ _PyArg_UnpackKeywordsWithVarargKwonly(PyObject *const *args, Py_ssize_t nargs, current_arg = NULL; } - buf[i - vararg] = current_arg; + /* This function only cares about keyword-only argument. + * If an arguments is passed in as a keyword argument, + * we do not process it. + * + * For example: + * def f(posonly, /, kw, *varargs, kwonly1, kwonly2): + * pass + * f(1, kw=2, kwonly1=3, kwonly2=4) + * + * This `buf` array should be: [3, 4]. */ + if (i >= vararg) { + buf[i - vararg] = current_arg; + } if (current_arg) { --nkwargs; @@ -2747,35 +2760,8 @@ _PyArg_UnpackKeywordsWithVarargKwonly(PyObject *const *args, Py_ssize_t nargs, } 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) ? "" : "()"); - } - return NULL; - } - } + error_unexpected_keyword_arg(kwargs, kwnames, kwtuple, parser->fname); + return NULL; } return buf; diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 83f8fb531d7462..11ef4bffc9ce15 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1038,8 +1038,8 @@ def parser_body(prototype, *fields, declarations=''): declarations += "\nPyObject *argsbuf[%s];" % argsbuf_size if vararg != NO_VARARG: declarations += "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) - parsed_argname = "fastargs" declarations += "\nPyObject *const *fastargs;" + parsed_argname = "fastargs" else: parsed_argname = "args" if has_optional_kw: From f63a70452f66e77e161308b7e6f57150b2393ec9 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Fri, 3 Feb 2023 15:54:36 +0800 Subject: [PATCH 14/22] Update news --- .../2022-01-04-08-09-32.bpo-46212._Fvxj6.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst index f92c906f5abd5d..104040ab3bfa15 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-08-09-32.bpo-46212._Fvxj6.rst @@ -1,4 +1,5 @@ -Avoid creating temporary varargs tuple in argument passing with "Argument Clinic generated code". +Avoid creating temporary varargs tuple in argument passing with Argument Clinic generated code. -Introduce a function `_PyArg_UnpackKeywordsWithVarargKwonly`, which parse positional args, varargs and keyword args, -but returns an array of ordered keyword args only. +Introduce the :c:func:`_PyArg_UnpackKeywordsWithVarargKwonly` function, +which checks and parses positional arguments, varargs, and keyword arguments, +but returns only parsed kwonly arguments. From 8f61f5e4a5421f8efc8830589cede4a9b72f7b48 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Mon, 6 Feb 2023 14:15:36 +0800 Subject: [PATCH 15/22] Fix varargssize in new_or_init --- Lib/test/clinic.test | 3 ++- Tools/clinic/clinic.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 01c2a55458022a..3127a728e78f96 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -4133,6 +4133,7 @@ static int TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; + Py_ssize_t varargssize = Py_MAX(PyTuple_GET_SIZE(args) - 0, 0); PyTypeObject *base_tp = &TestModule_Type; PyObject *const *__clinic_args; @@ -4155,7 +4156,7 @@ static int TestModule_TestClass1___init___impl(TestClassObject *self, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=89fc8cfb766b0097 input=3e093b8ea0b74b0c]*/ +/*[clinic end generated code: output=8ce5c8a0d4d3e0cb input=3e093b8ea0b74b0c]*/ /*[clinic input] diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 11ef4bffc9ce15..17ff8f0322d64d 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1190,7 +1190,11 @@ def parser_body(prototype, *fields, declarations=''): raise ValueError("Slot methods cannot access their defining class.") if not parses_keywords: - declarations = '{base_type_ptr}' + if not parser_body_declarations: + parser_body_declarations = '{base_type_ptr}' + else: + parser_body_declarations += '\n{base_type_ptr}' + fields.insert(0, normalize_snippet(""" if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{ goto exit; @@ -1204,7 +1208,7 @@ def parser_body(prototype, *fields, declarations=''): """, indent=4)) parser_definition = parser_body(parser_prototype, *fields, - declarations=declarations) + declarations=parser_body_declarations) if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'): From 1eff215c27941fab3cc7dfb54b57cb6ee41e15da Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Mon, 6 Feb 2023 14:41:42 +0800 Subject: [PATCH 16/22] Simplify the code a bit --- Lib/test/clinic.test | 16 ++++++++-------- Modules/clinic/_testclinic.c.h | 14 +++++++------- Python/clinic/bltinmodule.c.h | 4 ++-- Tools/clinic/clinic.py | 14 ++++++-------- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 3127a728e78f96..a59909ddf331b2 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -3845,8 +3845,8 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject }; #undef KWTUPLE PyObject *argsbuf[0]; - Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; PyObject *const *__clinic_args; @@ -3865,7 +3865,7 @@ exit: static PyObject * test_vararg_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=fb100afa02682813 input=81d33815ad1bae6e]*/ +/*[clinic end generated code: output=2f4921abd6707648 input=81d33815ad1bae6e]*/ /*[clinic input] test_vararg_with_default @@ -3920,9 +3920,9 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar }; #undef KWTUPLE PyObject *argsbuf[1]; - Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; PyObject *const *__clinic_args; int b = 0; @@ -3951,7 +3951,7 @@ static PyObject * test_vararg_with_default_impl(PyObject *module, PyObject *a, Py_ssize_t varargssize, PyObject *const *args, int b) -/*[clinic end generated code: output=c724b7a1b327f798 input=6e110b54acd9b22d]*/ +/*[clinic end generated code: output=60a4333c34883cc3 input=6e110b54acd9b22d]*/ /*[clinic input] test_vararg_with_only_defaults @@ -4006,9 +4006,9 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize }; #undef KWTUPLE PyObject *argsbuf[2]; - Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); PyObject *const *__clinic_args; int b = 0; PyObject *c = " "; @@ -4042,7 +4042,7 @@ static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=bdc061cc11a3abde input=fa56a709a035666e]*/ +/*[clinic end generated code: output=e68965b94622fbfc input=fa56a709a035666e]*/ /*[clinic input] test_paramname_module @@ -4205,8 +4205,8 @@ TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *argsbuf[1]; PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); - Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *pos; PyObject *const *__clinic_args; PyObject *kw = Py_None; @@ -4232,4 +4232,4 @@ static int TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, Py_ssize_t varargssize, PyObject *const *args, PyObject *kw) -/*[clinic end generated code: output=b7e8fba5e3e6e531 input=da8e0daa18983316]*/ +/*[clinic end generated code: output=2cfcce182118ec62 input=da8e0daa18983316]*/ diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 7d369cecafe9bb..6606c6a6d844f7 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2368,8 +2368,8 @@ posonly_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje }; #undef KWTUPLE PyObject *argsbuf[0]; - Py_ssize_t varargssize = Py_MAX(nargs - 2, 0); PyObject *const *fastargs; + Py_ssize_t varargssize = Py_MAX(nargs - 2, 0); PyObject *a; PyObject *b; PyObject *const *__clinic_args; @@ -2460,8 +2460,8 @@ vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwna }; #undef KWTUPLE PyObject *argsbuf[0]; - Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; PyObject *const *__clinic_args; @@ -2520,9 +2520,9 @@ vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nargs, P }; #undef KWTUPLE PyObject *argsbuf[1]; - Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *a; PyObject *const *__clinic_args; int b = 0; @@ -2589,9 +2589,9 @@ vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t na }; #undef KWTUPLE PyObject *argsbuf[1]; - Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); PyObject *const *__clinic_args; PyObject *b = Py_None; @@ -2655,9 +2655,9 @@ gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject }; #undef KWTUPLE PyObject *argsbuf[2]; - Py_ssize_t varargssize = Py_MAX(nargs - 2, 0); PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 2) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2; + Py_ssize_t varargssize = Py_MAX(nargs - 2, 0); PyObject *pos1; PyObject *pos2; PyObject *const *varargs; @@ -2732,9 +2732,9 @@ gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb }; #undef KWTUPLE PyObject *argsbuf[1]; - Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *const *fastargs; Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); PyObject *pos; PyObject *const *__clinic_args; PyObject *kw = Py_None; @@ -2818,4 +2818,4 @@ gh_99240_double_free(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=7a5b64c86324db97 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=d01570b22ee0815b input=a9049054013a1b77]*/ diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 6396dc9204f1ce..239fb12256d066 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -1081,9 +1081,9 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec }; #undef KWTUPLE PyObject *argsbuf[4]; - Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); PyObject *const *__clinic_args; PyObject *sep = Py_None; PyObject *end = Py_None; @@ -1411,4 +1411,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=86444999d5dd52a7 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7ef42ce13ab1f529 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 17ff8f0322d64d..2880136443308a 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1022,6 +1022,7 @@ def parser_body(prototype, *fields, declarations=''): ) nargs = "nargs" argsbuf_size = len(converters) + varargssize = "" else: args_declaration = "_PyArg_UnpackKeywordsWithVarargKwonly", "%s, %s, %s, %s" % ( min_pos, @@ -1031,13 +1032,13 @@ def parser_body(prototype, *fields, declarations=''): ) nargs = f"Py_MIN(nargs, {max_pos})" if max_pos else "0" argsbuf_size = len(converters) - vararg - 1 + varargssize = "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) + declarations = declare_parser(f) + declarations += "\nPyObject *argsbuf[%s];" % argsbuf_size if not new_or_init: flags = "METH_FASTCALL|METH_KEYWORDS" parser_prototype = parser_prototype_fastcall_keywords - declarations = declare_parser(f) - declarations += "\nPyObject *argsbuf[%s];" % argsbuf_size if vararg != NO_VARARG: - declarations += "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) declarations += "\nPyObject *const *fastargs;" parsed_argname = "fastargs" else: @@ -1055,13 +1056,8 @@ def parser_body(prototype, *fields, declarations=''): # positional-or-keyword arguments flags = "METH_VARARGS|METH_KEYWORDS" parser_prototype = parser_prototype_keyword - argname_fmt = 'fastargs[%d]' - declarations = declare_parser(f) - declarations += "\nPyObject *argsbuf[%s];" % argsbuf_size declarations += "\nPyObject * const *fastargs;" declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);" - if vararg != NO_VARARG: - declarations += "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) if has_optional_kw: declarations += "\nPy_ssize_t noptargs = %s + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - %d;" % (nargs, min_pos + min_kw_only) parser_code = [normalize_snippet(""" @@ -1070,6 +1066,8 @@ def parser_body(prototype, *fields, declarations=''): goto exit; }} """ % args_declaration, indent=4)] + argname_fmt = 'fastargs[%d]' + declarations += varargssize if requires_defining_class: flags = 'METH_METHOD|' + flags From 5baf0f5bc34f748dc5d5090e906e62b2620d25f8 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Mon, 6 Feb 2023 15:09:39 +0800 Subject: [PATCH 17/22] Optimize generated varargssize assignment --- Lib/test/clinic.test | 8 ++++---- Modules/clinic/_testclinic.c.h | 6 +++--- Python/clinic/bltinmodule.c.h | 4 ++-- Tools/clinic/clinic.py | 10 ++++++++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index a59909ddf331b2..69539c8d5220dd 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -4008,7 +4008,7 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize PyObject *argsbuf[2]; PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); + Py_ssize_t varargssize = nargs; PyObject *const *__clinic_args; int b = 0; PyObject *c = " "; @@ -4042,7 +4042,7 @@ static PyObject * test_vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, PyObject *const *args, int b, PyObject *c) -/*[clinic end generated code: output=e68965b94622fbfc input=fa56a709a035666e]*/ +/*[clinic end generated code: output=fd25c0b3171663cc input=fa56a709a035666e]*/ /*[clinic input] test_paramname_module @@ -4133,7 +4133,7 @@ static int TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; - Py_ssize_t varargssize = Py_MAX(PyTuple_GET_SIZE(args) - 0, 0); + Py_ssize_t varargssize = PyTuple_GET_SIZE(args); PyTypeObject *base_tp = &TestModule_Type; PyObject *const *__clinic_args; @@ -4156,7 +4156,7 @@ static int TestModule_TestClass1___init___impl(TestClassObject *self, Py_ssize_t varargssize, PyObject *const *args) -/*[clinic end generated code: output=8ce5c8a0d4d3e0cb input=3e093b8ea0b74b0c]*/ +/*[clinic end generated code: output=3f3f406f43e296d2 input=3e093b8ea0b74b0c]*/ /*[clinic input] diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 6606c6a6d844f7..1093ffa19ff33d 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2591,7 +2591,7 @@ vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t na PyObject *argsbuf[1]; PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); + Py_ssize_t varargssize = nargs; PyObject *const *__clinic_args; PyObject *b = Py_None; @@ -2773,7 +2773,7 @@ static PyObject * gh_99233_refcount(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; - Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); + Py_ssize_t varargssize = nargs; PyObject *const *__clinic_args; if (!_PyArg_CheckPositional("gh_99233_refcount", nargs, 0, PY_SSIZE_T_MAX)) { @@ -2818,4 +2818,4 @@ gh_99240_double_free(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=d01570b22ee0815b input=a9049054013a1b77]*/ +/*[clinic end generated code: output=55622d446938c069 input=a9049054013a1b77]*/ diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h index 239fb12256d066..13f4f7b56f8eca 100644 --- a/Python/clinic/bltinmodule.c.h +++ b/Python/clinic/bltinmodule.c.h @@ -1083,7 +1083,7 @@ builtin_print(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *argsbuf[4]; PyObject *const *fastargs; Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; - Py_ssize_t varargssize = Py_MAX(nargs - 0, 0); + Py_ssize_t varargssize = nargs; PyObject *const *__clinic_args; PyObject *sep = Py_None; PyObject *end = Py_None; @@ -1411,4 +1411,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=7ef42ce13ab1f529 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=637176d7f6fb764f input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 2880136443308a..d2914b7adf241c 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -945,7 +945,10 @@ def parser_body(prototype, *fields, declarations=''): argname_fmt = 'PyTuple_GET_ITEM(args, %d)' if vararg != NO_VARARG: - declarations = "Py_ssize_t varargssize = Py_MAX(%s - %d, 0);" % (nargs, max_pos) + if max_pos == 0: + declarations = "Py_ssize_t varargssize = %s;" % nargs + else: + declarations = "Py_ssize_t varargssize = Py_MAX(%s - %d, 0);" % (nargs, max_pos) else: declarations = "" @@ -1032,7 +1035,10 @@ def parser_body(prototype, *fields, declarations=''): ) nargs = f"Py_MIN(nargs, {max_pos})" if max_pos else "0" argsbuf_size = len(converters) - vararg - 1 - varargssize = "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % (max_pos) + if max_pos == 0: + varargssize = "\nPy_ssize_t varargssize = nargs;" + else: + varargssize = "\nPy_ssize_t varargssize = Py_MAX(nargs - %d, 0);" % max_pos declarations = declare_parser(f) declarations += "\nPyObject *argsbuf[%s];" % argsbuf_size if not new_or_init: From 30c06066a1ccef3101c4a12b088572e7e8778f4e Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Tue, 7 Feb 2023 10:37:43 +0800 Subject: [PATCH 18/22] Fix argument passing in new_or_init --- Lib/test/clinic.test | 6 +++--- Tools/clinic/clinic.py | 16 +++++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 69539c8d5220dd..52a45f4e249bb4 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -4215,8 +4215,8 @@ TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) if (!fastargs) { goto exit; } - pos = args[0]; - __clinic_args = args + 1; + pos = PyTuple_GET_ITEM(args, 0); + __clinic_args = _PyTuple_CAST(args)->ob_item + 1; if (!noptargs) { goto skip_optional_kwonly; } @@ -4232,4 +4232,4 @@ static int TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, Py_ssize_t varargssize, PyObject *const *args, PyObject *kw) -/*[clinic end generated code: output=2cfcce182118ec62 input=da8e0daa18983316]*/ +/*[clinic end generated code: output=e565fcea3dd35a86 input=da8e0daa18983316]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index d2914b7adf241c..4131fbfea02054 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1086,15 +1086,21 @@ def parser_body(prototype, *fields, declarations=''): "parameter (after self)") displayname = p.get_displayname(i+1) if vararg != NO_VARARG: - # positional args if i < int(vararg): - parsearg = p.converter.parse_arg('args[%d]' % i, displayname) - # keyword args + # positional args + if new_or_init: + parsearg = p.converter.parse_arg('PyTuple_GET_ITEM(args, %d)' % i, displayname) + else: + parsearg = p.converter.parse_arg('args[%d]' % i, displayname) elif i > int(vararg): + # keyword args parsearg = p.converter.parse_arg('fastargs[%d]' % (i - vararg - 1), displayname) - # vararg else: - parsearg = p.converter.parse_arg('args + %d' % vararg, p.converter.parser_name) + # vararg + if new_or_init: + parsearg = p.converter.parse_arg('_PyTuple_CAST(args)->ob_item + %d' % vararg, p.converter.parser_name) + else: + parsearg = p.converter.parse_arg('args + %d' % vararg, p.converter.parser_name) else: parsearg = p.converter.parse_arg(argname_fmt % i, displayname) if parsearg is None: From d9e7408260891e3d69e0803acca8b3c33b077591 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Tue, 7 Feb 2023 12:00:55 +0800 Subject: [PATCH 19/22] Add tests for class method `__new__` --- Lib/test/clinic.test | 14 ++-- Lib/test/test_clinic.py | 18 +++++ Modules/_testclinic.c | 110 +++++++++++++++++++++++++ Modules/clinic/_testclinic.c.h | 143 ++++++++++++++++++++++++++++++++- 4 files changed, 277 insertions(+), 8 deletions(-) diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index 52a45f4e249bb4..c8c92b30f26d64 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -4160,7 +4160,7 @@ TestModule_TestClass1___init___impl(TestClassObject *self, /*[clinic input] -TestModule.TestClass1.__init__ +TestModule.TestClass2.__init__ pos: object *args: object @@ -4169,12 +4169,12 @@ TestModule.TestClass1.__init__ [clinic start generated code]*/ static int -TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, +TestModule_TestClass2___init___impl(TestClassObject *self, PyObject *pos, Py_ssize_t varargssize, PyObject *const *args, PyObject *kw); static int -TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) +TestModule_TestClass2___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -4198,7 +4198,7 @@ TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) static const char * const _keywords[] = {"pos", "kw", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, - .fname = "TestClass1", + .fname = "TestClass2", .kwtuple = KWTUPLE, }; #undef KWTUPLE @@ -4222,14 +4222,14 @@ TestModule_TestClass1___init__(PyObject *self, PyObject *args, PyObject *kwargs) } kw = fastargs[0]; skip_optional_kwonly: - return_value = TestModule_TestClass1___init___impl((TestClassObject *)self, pos, varargssize, __clinic_args, kw); + return_value = TestModule_TestClass2___init___impl((TestClassObject *)self, pos, varargssize, __clinic_args, kw); exit: return return_value; } static int -TestModule_TestClass1___init___impl(TestClassObject *self, PyObject *pos, +TestModule_TestClass2___init___impl(TestClassObject *self, PyObject *pos, Py_ssize_t varargssize, PyObject *const *args, PyObject *kw) -/*[clinic end generated code: output=e565fcea3dd35a86 input=da8e0daa18983316]*/ +/*[clinic end generated code: output=335f080bcb0946ab input=833936c1d51c4329]*/ diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 4abf739cf52ca3..2199ace375b21f 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1266,6 +1266,24 @@ def test_vararg_with_only_defaults(self): self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4), ((1, 2, 3, 4), None)) self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4, b=5), ((1, 2, 3, 4), 5)) + def test_class_new_vararg_only(self): + self.assertEqual(ac_tester.class_new_vararg_only(), ((), )) + self.assertEqual(ac_tester.class_new_vararg_only(1, 2, 3, 4), ((1, 2, 3, 4), )) + + def test_class_new_vararg(self): + with self.assertRaises(TypeError): + ac_tester.class_new_vararg() + with self.assertRaises(TypeError): + ac_tester.class_new_vararg(1, b=2) + self.assertEqual(ac_tester.class_new_vararg(1, 2, 3, 4), (1, (2, 3, 4))) + + def test_vararg_with_default(self): + with self.assertRaises(TypeError): + ac_tester.class_new_vararg_with_default() + self.assertEqual(ac_tester.class_new_vararg_with_default(1, b=2), (1, (), 2)) + self.assertEqual(ac_tester.class_new_vararg_with_default(1, 2, 3, 4), (1, (2, 3, 4), None)) + self.assertEqual(ac_tester.class_new_vararg_with_default(1, 2, 3, 4, b=5), (1, (2, 3, 4), 5)) + def test_gh_32092_oob(self): ac_tester.gh_32092_oob(1, 2, 3, 4, kw1=5, kw2=6) diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index adab7d0db5b356..4474bebe09a408 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -9,6 +9,12 @@ #include "Python.h" +typedef struct TestClassObject { + PyObject_HEAD +} TestClassObject; + +static PyTypeObject TestClass_Type; + #include "clinic/_testclinic.c.h" @@ -1086,6 +1092,98 @@ vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, +static PyTypeObject TestClass_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "TestClassType", + .tp_basicsize = sizeof(TestClassObject), + .tp_flags = Py_TPFLAGS_DEFAULT, +}; + +/*[clinic input] +class _testclinic.TestClassVararg "TestClassObject *" "&TestClass_Type" +class _testclinic.TestClassVarargOnly "TestClassObject *" "&TestClass_Type" +class _testclinic.TestClassVarargWithDefault "TestClassObject *" "&TestClass_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=97c670b477b79c5a]*/ + + +/*[clinic input] +@classmethod +_testclinic.TestClassVarargOnly.__new__ + + *args: object + +[clinic start generated code]*/ + +static PyObject * +_testclinic_TestClassVarargOnly_impl(PyTypeObject *type, + Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=05947a2aa5f8adff input=584464d2a3960b1b]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(1, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; +} + + +/*[clinic input] +@classmethod +_testclinic.TestClassVararg.__new__ + + a: object + *args: object + +[clinic start generated code]*/ + +static PyObject * +_testclinic_TestClassVararg_impl(PyTypeObject *type, PyObject *a, + Py_ssize_t varargssize, + PyObject *const *args) +/*[clinic end generated code: output=631d42b9183732a2 input=88e1a6be73982e01]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(2, a, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; +} + + +/*[clinic input] +@classmethod +_testclinic.TestClassVarargWithDefault.__new__ + + a: object + *args: object + b: object = None + +[clinic start generated code]*/ + +static PyObject * +_testclinic_TestClassVarargWithDefault_impl(PyTypeObject *type, PyObject *a, + Py_ssize_t varargssize, + PyObject *const *args, + PyObject *b) +/*[clinic end generated code: output=f91a821bb9f592a9 input=19c364f334bc623e]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, args); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(3, a, vararg_tuple, b); + Py_DECREF(vararg_tuple); + return result; +} + + + /*[clinic input] gh_32092_oob @@ -1215,6 +1313,18 @@ static PyMethodDef tester_methods[] = { VARARG_METHODDEF VARARG_WITH_DEFAULT_METHODDEF VARARG_WITH_ONLY_DEFAULTS_METHODDEF + {"class_new_vararg_only", + _PyCFunction_CAST(_testclinic_TestClassVarargOnly), + METH_VARARGS | METH_KEYWORDS, + PyDoc_STR("Test vararg parsing in class method `__new__`.")}, + {"class_new_vararg", + _PyCFunction_CAST(_testclinic_TestClassVararg), + METH_VARARGS | METH_KEYWORDS, + PyDoc_STR("Test posargs and vararg parsing in class method `__new__`.")}, + {"class_new_vararg_with_default", + _PyCFunction_CAST(_testclinic_TestClassVarargWithDefault), + METH_VARARGS | METH_KEYWORDS, + PyDoc_STR("Test vararg and kwargs parsing in class method `__new__`.")}, GH_32092_OOB_METHODDEF GH_32092_KW_PASS_METHODDEF GH_99233_REFCOUNT_METHODDEF diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 1093ffa19ff33d..243f365badf04f 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2611,6 +2611,147 @@ vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t na return return_value; } +static PyObject * +_testclinic_TestClassVarargOnly_impl(PyTypeObject *type, + Py_ssize_t varargssize, + PyObject *const *args); + +static PyObject * +_testclinic_TestClassVarargOnly(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + Py_ssize_t varargssize = PyTuple_GET_SIZE(args); + PyTypeObject *base_tp = &TestClass_Type; + PyObject *const *__clinic_args; + + if ((type == base_tp || type->tp_init == base_tp->tp_init) && + !_PyArg_NoKeywords("TestClassVarargOnly", kwargs)) { + goto exit; + } + if (!_PyArg_CheckPositional("TestClassVarargOnly", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) { + goto exit; + } + __clinic_args = _PyTuple_CAST(args)->ob_item; + return_value = _testclinic_TestClassVarargOnly_impl(type, varargssize, __clinic_args); + +exit: + return return_value; +} + +static PyObject * +_testclinic_TestClassVararg_impl(PyTypeObject *type, PyObject *a, + Py_ssize_t varargssize, + PyObject *const *args); + +static PyObject * +_testclinic_TestClassVararg(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 1 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(a), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "TestClassVararg", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[0]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *a; + PyObject *const *__clinic_args; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + __clinic_args = _PyTuple_CAST(args)->ob_item + 1; + return_value = _testclinic_TestClassVararg_impl(type, a, varargssize, __clinic_args); + +exit: + return return_value; +} + +static PyObject * +_testclinic_TestClassVarargWithDefault_impl(PyTypeObject *type, PyObject *a, + Py_ssize_t varargssize, + PyObject *const *args, + PyObject *b); + +static PyObject * +_testclinic_TestClassVarargWithDefault(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(a), &_Py_ID(b), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "TestClassVarargWithDefault", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *a; + PyObject *const *__clinic_args; + PyObject *b = Py_None; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + a = PyTuple_GET_ITEM(args, 0); + __clinic_args = _PyTuple_CAST(args)->ob_item + 1; + if (!noptargs) { + goto skip_optional_kwonly; + } + b = fastargs[0]; +skip_optional_kwonly: + return_value = _testclinic_TestClassVarargWithDefault_impl(type, a, varargssize, __clinic_args, b); + +exit: + return return_value; +} + PyDoc_STRVAR(gh_32092_oob__doc__, "gh_32092_oob($module, /, pos1, pos2, *varargs, kw1=None, kw2=None)\n" "--\n" @@ -2818,4 +2959,4 @@ gh_99240_double_free(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=55622d446938c069 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c000f8420ea57228 input=a9049054013a1b77]*/ From 85a8ac5a7c8f2120ea01d0040726867052a6e6b0 Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Wed, 8 Feb 2023 11:23:39 +0800 Subject: [PATCH 20/22] Correct test function name --- Lib/test/test_clinic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 2199ace375b21f..e18b276138b456 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1277,7 +1277,7 @@ def test_class_new_vararg(self): ac_tester.class_new_vararg(1, b=2) self.assertEqual(ac_tester.class_new_vararg(1, 2, 3, 4), (1, (2, 3, 4))) - def test_vararg_with_default(self): + def test_class_new_vararg_with_default(self): with self.assertRaises(TypeError): ac_tester.class_new_vararg_with_default() self.assertEqual(ac_tester.class_new_vararg_with_default(1, b=2), (1, (), 2)) From a9d1424fb9ca4f304748a8635693ff97c1608e3f Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Wed, 8 Feb 2023 11:36:04 +0800 Subject: [PATCH 21/22] Fix vararg parsing when its name is not `args` --- Lib/test/clinic.test | 130 +++++++++++++++++++++++++++++++++ Lib/test/test_clinic.py | 11 +++ Modules/_testclinic.c | 51 +++++++++++++ Modules/clinic/_testclinic.c.h | 102 +++++++++++++++++++++++++- Tools/clinic/clinic.py | 3 +- 5 files changed, 294 insertions(+), 3 deletions(-) diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test index c8c92b30f26d64..c219a909d352a1 100644 --- a/Lib/test/clinic.test +++ b/Lib/test/clinic.test @@ -4108,6 +4108,136 @@ static PyObject * test_paramname_module_impl(PyObject *module, PyObject *mod) /*[clinic end generated code: output=4a2a849ecbcc8b53 input=afefe259667f13ba]*/ +/*[clinic input] +test_vararg_with_other_name + + *strange_name_vararg: object + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_vararg_with_other_name__doc__, +"test_vararg_with_other_name($module, /, *strange_name_vararg)\n" +"--\n" +"\n"); + +#define TEST_VARARG_WITH_OTHER_NAME_METHODDEF \ + {"test_vararg_with_other_name", _PyCFunction_CAST(test_vararg_with_other_name), METH_FASTCALL, test_vararg_with_other_name__doc__}, + +static PyObject * +test_vararg_with_other_name_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *strange_name_vararg); + +static PyObject * +test_vararg_with_other_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t varargssize = nargs; + PyObject *const *strange_name_vararg; + + if (!_PyArg_CheckPositional("test_vararg_with_other_name", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + strange_name_vararg = args + 0; + return_value = test_vararg_with_other_name_impl(module, varargssize, strange_name_vararg); + +exit: + return return_value; +} + +static PyObject * +test_vararg_with_other_name_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *strange_name_vararg) +/*[clinic end generated code: output=7340df6f8a2b95a8 input=44747a55460ea494]*/ + +/*[clinic input] +test_vararg_with_default_with_other_name + + a as other_a: object + *args as other_args: object + b as other_b: bool = False + +[clinic start generated code]*/ + +PyDoc_STRVAR(test_vararg_with_default_with_other_name__doc__, +"test_vararg_with_default_with_other_name($module, /, a, *args, b=False)\n" +"--\n" +"\n"); + +#define TEST_VARARG_WITH_DEFAULT_WITH_OTHER_NAME_METHODDEF \ + {"test_vararg_with_default_with_other_name", _PyCFunction_CAST(test_vararg_with_default_with_other_name), METH_FASTCALL|METH_KEYWORDS, test_vararg_with_default_with_other_name__doc__}, + +static PyObject * +test_vararg_with_default_with_other_name_impl(PyObject *module, + PyObject *other_a, + Py_ssize_t varargssize, + PyObject *const *other_args, + int other_b); + +static PyObject * +test_vararg_with_default_with_other_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(a), &_Py_ID(b), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "test_vararg_with_default_with_other_name", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *const *fastargs; + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *other_a; + PyObject *const *other_args; + int other_b = 0; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + other_a = args[0]; + other_args = args + 1; + if (!noptargs) { + goto skip_optional_kwonly; + } + other_b = PyObject_IsTrue(fastargs[0]); + if (other_b < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = test_vararg_with_default_with_other_name_impl(module, other_a, varargssize, other_args, other_b); + +exit: + return return_value; +} + +static PyObject * +test_vararg_with_default_with_other_name_impl(PyObject *module, + PyObject *other_a, + Py_ssize_t varargssize, + PyObject *const *other_args, + int other_b) +/*[clinic end generated code: output=d2149d17eba67671 input=779a17a90096c922]*/ + /*[clinic input] module TestModule diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index e18b276138b456..c25f9c5b534327 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1266,6 +1266,17 @@ def test_vararg_with_only_defaults(self): self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4), ((1, 2, 3, 4), None)) self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4, b=5), ((1, 2, 3, 4), 5)) + def test_vararg_with_other_name(self): + self.assertEqual(ac_tester.vararg_with_other_name(), ((), )) + self.assertEqual(ac_tester.vararg_with_other_name(1, 2, 3, 4), ((1, 2, 3, 4), )) + + def test_vararg_with_default_with_other_name(self): + with self.assertRaises(TypeError): + ac_tester.vararg_with_default_with_other_name() + self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, b=False), (1, (), False)) + self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, 2, 3, 4), (1, (2, 3, 4), False)) + self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, 2, 3, 4, b=True), (1, (2, 3, 4), True)) + def test_class_new_vararg_only(self): self.assertEqual(ac_tester.class_new_vararg_only(), ((), )) self.assertEqual(ac_tester.class_new_vararg_only(1, 2, 3, 4), ((1, 2, 3, 4), )) diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c index 4474bebe09a408..6f118d027d98db 100644 --- a/Modules/_testclinic.c +++ b/Modules/_testclinic.c @@ -1091,6 +1091,55 @@ vararg_with_only_defaults_impl(PyObject *module, Py_ssize_t varargssize, } +/*[clinic input] +vararg_with_other_name + + *strange_name_vararg: object + +[clinic start generated code]*/ + +static PyObject * +vararg_with_other_name_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *strange_name_vararg) +/*[clinic end generated code: output=73a50a717838e561 input=db56857a45dfa50e]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, strange_name_vararg); + if (!vararg_tuple) { + return NULL; + } + PyObject *result = pack_arguments_newref(1, vararg_tuple); + Py_DECREF(vararg_tuple); + return result; +} + + +/*[clinic input] +vararg_with_default_with_other_name + + a as other_a: object + *args as other_args: object + b as other_b: bool = False + +[clinic start generated code]*/ + +static PyObject * +vararg_with_default_with_other_name_impl(PyObject *module, PyObject *other_a, + Py_ssize_t varargssize, + PyObject *const *other_args, + int other_b) +/*[clinic end generated code: output=a7ac0fa0c132a04f input=b895540d71cd74f9]*/ +{ + PyObject *vararg_tuple = pack_varargs_to_tuple(varargssize, other_args); + if (!vararg_tuple) { + return NULL; + } + PyObject *obj_b = other_b ? Py_True : Py_False; + PyObject *result = pack_arguments_newref(3, other_a, vararg_tuple, obj_b); + Py_DECREF(vararg_tuple); + return result; +} + + static PyTypeObject TestClass_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -1313,6 +1362,8 @@ static PyMethodDef tester_methods[] = { VARARG_METHODDEF VARARG_WITH_DEFAULT_METHODDEF VARARG_WITH_ONLY_DEFAULTS_METHODDEF + VARARG_WITH_OTHER_NAME_METHODDEF + VARARG_WITH_DEFAULT_WITH_OTHER_NAME_METHODDEF {"class_new_vararg_only", _PyCFunction_CAST(_testclinic_TestClassVarargOnly), METH_VARARGS | METH_KEYWORDS, diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h index 243f365badf04f..4dd2d86dfdb6c0 100644 --- a/Modules/clinic/_testclinic.c.h +++ b/Modules/clinic/_testclinic.c.h @@ -2611,6 +2611,106 @@ vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t na return return_value; } +PyDoc_STRVAR(vararg_with_other_name__doc__, +"vararg_with_other_name($module, /, *strange_name_vararg)\n" +"--\n" +"\n"); + +#define VARARG_WITH_OTHER_NAME_METHODDEF \ + {"vararg_with_other_name", _PyCFunction_CAST(vararg_with_other_name), METH_FASTCALL, vararg_with_other_name__doc__}, + +static PyObject * +vararg_with_other_name_impl(PyObject *module, Py_ssize_t varargssize, + PyObject *const *strange_name_vararg); + +static PyObject * +vararg_with_other_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *return_value = NULL; + Py_ssize_t varargssize = nargs; + PyObject *const *strange_name_vararg; + + if (!_PyArg_CheckPositional("vararg_with_other_name", nargs, 0, PY_SSIZE_T_MAX)) { + goto exit; + } + strange_name_vararg = args + 0; + return_value = vararg_with_other_name_impl(module, varargssize, strange_name_vararg); + +exit: + return return_value; +} + +PyDoc_STRVAR(vararg_with_default_with_other_name__doc__, +"vararg_with_default_with_other_name($module, /, a, *args, b=False)\n" +"--\n" +"\n"); + +#define VARARG_WITH_DEFAULT_WITH_OTHER_NAME_METHODDEF \ + {"vararg_with_default_with_other_name", _PyCFunction_CAST(vararg_with_default_with_other_name), METH_FASTCALL|METH_KEYWORDS, vararg_with_default_with_other_name__doc__}, + +static PyObject * +vararg_with_default_with_other_name_impl(PyObject *module, PyObject *other_a, + Py_ssize_t varargssize, + PyObject *const *other_args, + int other_b); + +static PyObject * +vararg_with_default_with_other_name(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) + + #define NUM_KEYWORDS 2 + static struct { + PyGC_Head _this_is_not_used; + PyObject_VAR_HEAD + PyObject *ob_item[NUM_KEYWORDS]; + } _kwtuple = { + .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) + .ob_item = { &_Py_ID(a), &_Py_ID(b), }, + }; + #undef NUM_KEYWORDS + #define KWTUPLE (&_kwtuple.ob_base.ob_base) + + #else // !Py_BUILD_CORE + # define KWTUPLE NULL + #endif // !Py_BUILD_CORE + + static const char * const _keywords[] = {"a", "b", NULL}; + static _PyArg_Parser _parser = { + .keywords = _keywords, + .fname = "vararg_with_default_with_other_name", + .kwtuple = KWTUPLE, + }; + #undef KWTUPLE + PyObject *argsbuf[1]; + PyObject *const *fastargs; + Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_ssize_t varargssize = Py_MAX(nargs - 1, 0); + PyObject *other_a; + PyObject *const *other_args; + int other_b = 0; + + fastargs = _PyArg_UnpackKeywordsWithVarargKwonly(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); + if (!fastargs) { + goto exit; + } + other_a = args[0]; + other_args = args + 1; + if (!noptargs) { + goto skip_optional_kwonly; + } + other_b = PyObject_IsTrue(fastargs[0]); + if (other_b < 0) { + goto exit; + } +skip_optional_kwonly: + return_value = vararg_with_default_with_other_name_impl(module, other_a, varargssize, other_args, other_b); + +exit: + return return_value; +} + static PyObject * _testclinic_TestClassVarargOnly_impl(PyTypeObject *type, Py_ssize_t varargssize, @@ -2959,4 +3059,4 @@ gh_99240_double_free(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=c000f8420ea57228 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=bba2892730e7f50a input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 4131fbfea02054..7f7b6a1ebb6725 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -968,10 +968,9 @@ def parser_body(prototype, *fields, declarations=''): if p.is_vararg(): if not new_or_init: parser_code.append(normalize_snippet(""" - %s = %s + %d; + %s = args + %d; """ % ( p.converter.parser_name, - p.name, vararg ), indent=4)) else: From 597152e2b3e5ae941dbd2d7a0d4e7ef5995308ad Mon Sep 17 00:00:00 2001 From: colorfulappl Date: Wed, 8 Feb 2023 13:05:11 +0800 Subject: [PATCH 22/22] Add testcases for undeclared keyword arguments --- Lib/test/test_clinic.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index c25f9c5b534327..076e6dffbf5e57 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1234,6 +1234,8 @@ def test_keyword_only_parameter(self): def test_posonly_vararg(self): with self.assertRaises(TypeError): ac_tester.posonly_vararg() + with self.assertRaises(TypeError): + ac_tester.posonly_vararg(1, invalid_kw=2) self.assertEqual(ac_tester.posonly_vararg(1, 2), (1, 2, ())) self.assertEqual(ac_tester.posonly_vararg(1, b=2), (1, 2, ())) self.assertEqual(ac_tester.posonly_vararg(1, 2, 3, 4), (1, 2, (3, 4))) @@ -1242,24 +1244,28 @@ def test_vararg_and_posonly(self): with self.assertRaises(TypeError): ac_tester.vararg_and_posonly() with self.assertRaises(TypeError): - ac_tester.vararg_and_posonly(1, b=2) + ac_tester.vararg_and_posonly(1, invalid_kw=2) self.assertEqual(ac_tester.vararg_and_posonly(1, 2, 3, 4), (1, (2, 3, 4))) def test_vararg(self): with self.assertRaises(TypeError): ac_tester.vararg() with self.assertRaises(TypeError): - ac_tester.vararg(1, b=2) + ac_tester.vararg(1, invalid_kw=2) self.assertEqual(ac_tester.vararg(1, 2, 3, 4), (1, (2, 3, 4))) def test_vararg_with_default(self): with self.assertRaises(TypeError): ac_tester.vararg_with_default() + with self.assertRaises(TypeError): + ac_tester.vararg_with_default(1, invalid_kw=1) self.assertEqual(ac_tester.vararg_with_default(1, b=False), (1, (), False)) self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4), (1, (2, 3, 4), False)) self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4, b=True), (1, (2, 3, 4), True)) def test_vararg_with_only_defaults(self): + with self.assertRaises(TypeError): + ac_tester.vararg_with_only_defaults(invalid_kw=1) self.assertEqual(ac_tester.vararg_with_only_defaults(), ((), None)) self.assertEqual(ac_tester.vararg_with_only_defaults(b=2), ((), 2)) self.assertEqual(ac_tester.vararg_with_only_defaults(1, b=2), ((1, ), 2)) @@ -1267,17 +1273,24 @@ def test_vararg_with_only_defaults(self): self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4, b=5), ((1, 2, 3, 4), 5)) def test_vararg_with_other_name(self): + with self.assertRaises(TypeError): + ac_tester.vararg_with_other_name(invalid_kw=2) self.assertEqual(ac_tester.vararg_with_other_name(), ((), )) self.assertEqual(ac_tester.vararg_with_other_name(1, 2, 3, 4), ((1, 2, 3, 4), )) def test_vararg_with_default_with_other_name(self): with self.assertRaises(TypeError): ac_tester.vararg_with_default_with_other_name() + with self.assertRaises(TypeError): + ac_tester.vararg_with_default_with_other_name(1, invalid_kw=2) self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, b=False), (1, (), False)) self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, 2, 3, 4), (1, (2, 3, 4), False)) self.assertEqual(ac_tester.vararg_with_default_with_other_name(1, 2, 3, 4, b=True), (1, (2, 3, 4), True)) def test_class_new_vararg_only(self): + # TODO: Here a TypeError should be raised + # with self.assertRaises(TypeError): + # ac_tester.class_new_vararg_only(invalid_kw=1) self.assertEqual(ac_tester.class_new_vararg_only(), ((), )) self.assertEqual(ac_tester.class_new_vararg_only(1, 2, 3, 4), ((1, 2, 3, 4), )) @@ -1285,12 +1298,14 @@ def test_class_new_vararg(self): with self.assertRaises(TypeError): ac_tester.class_new_vararg() with self.assertRaises(TypeError): - ac_tester.class_new_vararg(1, b=2) + ac_tester.class_new_vararg(1, invalid_kw=2) self.assertEqual(ac_tester.class_new_vararg(1, 2, 3, 4), (1, (2, 3, 4))) def test_class_new_vararg_with_default(self): with self.assertRaises(TypeError): ac_tester.class_new_vararg_with_default() + with self.assertRaises(TypeError): + ac_tester.class_new_vararg_with_default(1, invalid_kw=2) self.assertEqual(ac_tester.class_new_vararg_with_default(1, b=2), (1, (), 2)) self.assertEqual(ac_tester.class_new_vararg_with_default(1, 2, 3, 4), (1, (2, 3, 4), None)) self.assertEqual(ac_tester.class_new_vararg_with_default(1, 2, 3, 4, b=5), (1, (2, 3, 4), 5))