From 01ca7b2e5ecc2455cd8453c8334ead6d0ab3a708 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 30 Apr 2022 13:15:02 +0300 Subject: [PATCH] [3.9] gh-91583: AC: Fix regression for functions with defining_class (GH-91739) Argument Clinic now generates the same efficient code as before adding the defining_class parameter.. (cherry picked from commit a055dac0b45031878a8196a8735522de018491e3) Co-authored-by: Serhiy Storchaka --- ...2-04-20-14-26-14.gh-issue-91583.200qI0.rst | 2 + Modules/clinic/_testmultiphase.c.h | 66 +++++++++++-------- Modules/clinic/posixmodule.c.h | 56 ++++++++++++---- Tools/clinic/clinic.py | 53 ++++++++++----- 4 files changed, 120 insertions(+), 57 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2022-04-20-14-26-14.gh-issue-91583.200qI0.rst diff --git a/Misc/NEWS.d/next/Tools-Demos/2022-04-20-14-26-14.gh-issue-91583.200qI0.rst b/Misc/NEWS.d/next/Tools-Demos/2022-04-20-14-26-14.gh-issue-91583.200qI0.rst new file mode 100644 index 00000000000000..bdfa71100f95ae --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2022-04-20-14-26-14.gh-issue-91583.200qI0.rst @@ -0,0 +1,2 @@ +Fix regression in the code generated by Argument Clinic for functions with +the ``defining_class`` parameter. diff --git a/Modules/clinic/_testmultiphase.c.h b/Modules/clinic/_testmultiphase.c.h index 0d38c230f71865..23180d0aa1373a 100644 --- a/Modules/clinic/_testmultiphase.c.h +++ b/Modules/clinic/_testmultiphase.c.h @@ -18,18 +18,11 @@ _testmultiphase_StateAccessType_get_defining_module_impl(StateAccessTypeObject * static PyObject * _testmultiphase_StateAccessType_get_defining_module(StateAccessTypeObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *return_value = NULL; - static const char * const _keywords[] = { NULL}; - static _PyArg_Parser _parser = {":get_defining_module", _keywords, 0}; - - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser - )) { - goto exit; + if (nargs) { + PyErr_SetString(PyExc_TypeError, "get_defining_module() takes no arguments"); + return NULL; } - return_value = _testmultiphase_StateAccessType_get_defining_module_impl(self, cls); - -exit: - return return_value; + return _testmultiphase_StateAccessType_get_defining_module_impl(self, cls); } PyDoc_STRVAR(_testmultiphase_StateAccessType_increment_count_clinic__doc__, @@ -55,14 +48,42 @@ _testmultiphase_StateAccessType_increment_count_clinic(StateAccessTypeObject *se { PyObject *return_value = NULL; static const char * const _keywords[] = {"n", "twice", NULL}; - static _PyArg_Parser _parser = {"|i$p:increment_count_clinic", _keywords, 0}; + static _PyArg_Parser _parser = {NULL, _keywords, "increment_count_clinic", 0}; + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int n = 1; int twice = 0; - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &n, &twice)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { goto exit; } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + if (PyFloat_Check(args[0])) { + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + } + n = _PyLong_AsInt(args[0]); + if (n == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } +skip_optional_pos: + if (!noptargs) { + goto skip_optional_kwonly; + } + twice = PyObject_IsTrue(args[1]); + if (twice < 0) { + goto exit; + } +skip_optional_kwonly: return_value = _testmultiphase_StateAccessType_increment_count_clinic_impl(self, cls, n, twice); exit: @@ -85,17 +106,10 @@ _testmultiphase_StateAccessType_get_count_impl(StateAccessTypeObject *self, static PyObject * _testmultiphase_StateAccessType_get_count(StateAccessTypeObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *return_value = NULL; - static const char * const _keywords[] = { NULL}; - static _PyArg_Parser _parser = {":get_count", _keywords, 0}; - - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser - )) { - goto exit; + if (nargs) { + PyErr_SetString(PyExc_TypeError, "get_count() takes no arguments"); + return NULL; } - return_value = _testmultiphase_StateAccessType_get_count_impl(self, cls); - -exit: - return return_value; + return _testmultiphase_StateAccessType_get_count_impl(self, cls); } -/*[clinic end generated code: output=39eea487e94e7f5d input=a9049054013a1b77]*/ +/*[clinic end generated code: output=60d68e2336064f96 input=a9049054013a1b77]*/ diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 28798ab99c1a00..f5df5c3b907ba4 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -8438,12 +8438,10 @@ static PyObject * os_DirEntry_is_symlink(DirEntry *self, PyTypeObject *defining_class, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = { NULL}; - static _PyArg_Parser _parser = {":is_symlink", _keywords, 0}; int _return_value; - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser - )) { + if (nargs) { + PyErr_SetString(PyExc_TypeError, "is_symlink() takes no arguments"); goto exit; } _return_value = os_DirEntry_is_symlink_impl(self, defining_class); @@ -8474,13 +8472,23 @@ os_DirEntry_stat(DirEntry *self, PyTypeObject *defining_class, PyObject *const * { PyObject *return_value = NULL; static const char * const _keywords[] = {"follow_symlinks", NULL}; - static _PyArg_Parser _parser = {"|$p:stat", _keywords, 0}; + static _PyArg_Parser _parser = {NULL, _keywords, "stat", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int follow_symlinks = 1; - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &follow_symlinks)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!args) { goto exit; } + if (!noptargs) { + goto skip_optional_kwonly; + } + follow_symlinks = PyObject_IsTrue(args[0]); + if (follow_symlinks < 0) { + goto exit; + } +skip_optional_kwonly: return_value = os_DirEntry_stat_impl(self, defining_class, follow_symlinks); exit: @@ -8505,14 +8513,24 @@ os_DirEntry_is_dir(DirEntry *self, PyTypeObject *defining_class, PyObject *const { PyObject *return_value = NULL; static const char * const _keywords[] = {"follow_symlinks", NULL}; - static _PyArg_Parser _parser = {"|$p:is_dir", _keywords, 0}; + static _PyArg_Parser _parser = {NULL, _keywords, "is_dir", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int follow_symlinks = 1; int _return_value; - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &follow_symlinks)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!args) { goto exit; } + if (!noptargs) { + goto skip_optional_kwonly; + } + follow_symlinks = PyObject_IsTrue(args[0]); + if (follow_symlinks < 0) { + goto exit; + } +skip_optional_kwonly: _return_value = os_DirEntry_is_dir_impl(self, defining_class, follow_symlinks); if ((_return_value == -1) && PyErr_Occurred()) { goto exit; @@ -8541,14 +8559,24 @@ os_DirEntry_is_file(DirEntry *self, PyTypeObject *defining_class, PyObject *cons { PyObject *return_value = NULL; static const char * const _keywords[] = {"follow_symlinks", NULL}; - static _PyArg_Parser _parser = {"|$p:is_file", _keywords, 0}; + static _PyArg_Parser _parser = {NULL, _keywords, "is_file", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int follow_symlinks = 1; int _return_value; - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &follow_symlinks)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf); + if (!args) { goto exit; } + if (!noptargs) { + goto skip_optional_kwonly; + } + follow_symlinks = PyObject_IsTrue(args[0]); + if (follow_symlinks < 0) { + goto exit; + } +skip_optional_kwonly: _return_value = os_DirEntry_is_file_impl(self, defining_class, follow_symlinks); if ((_return_value == -1) && PyErr_Occurred()) { goto exit; @@ -9441,4 +9469,4 @@ os_waitstatus_to_exitcode(PyObject *module, PyObject *const *args, Py_ssize_t na #ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF #define OS_WAITSTATUS_TO_EXITCODE_METHODDEF #endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */ -/*[clinic end generated code: output=c7c8796918b09139 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=08879489d9e259a1 input=a9049054013a1b77]*/ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 61d30b01c0998d..655e386512a581 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -636,6 +636,10 @@ def output_templates(self, f): assert parameters assert isinstance(parameters[0].converter, self_converter) del parameters[0] + requires_defining_class = False + if parameters and isinstance(parameters[0].converter, defining_class_converter): + requires_defining_class = True + del parameters[0] converters = [p.converter for p in parameters] has_option_groups = parameters and (parameters[0].group or parameters[-1].group) @@ -657,10 +661,6 @@ def output_templates(self, f): if not p.is_optional(): min_pos = i - requires_defining_class = any( - isinstance(p.converter, defining_class_converter) - for p in parameters) - meth_o = (len(parameters) == 1 and parameters[0].is_positional_only() and not converters[0].is_optional() and @@ -763,24 +763,40 @@ def parser_body(prototype, *fields, declarations=''): return linear_format(output(), parser_declarations=declarations) if not parameters: - # no parameters, METH_NOARGS + if not requires_defining_class: + # no parameters, METH_NOARGS + flags = "METH_NOARGS" - flags = "METH_NOARGS" + parser_prototype = normalize_snippet(""" + static PyObject * + {c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) + """) + parser_code = [] - parser_prototype = normalize_snippet(""" - static PyObject * - {c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) - """) - parser_definition = parser_prototype + else: + assert not new_or_init - if default_return_converter: - parser_definition = parser_prototype + '\n' + normalize_snippet(""" - {{ - return {c_basename}_impl({impl_arguments}); + flags = "METH_METHOD|METH_FASTCALL|METH_KEYWORDS" + + parser_prototype = parser_prototype_def_class + return_error = ('return NULL;' if default_return_converter + else 'goto exit;') + parser_code = [normalize_snippet(""" + if (nargs) {{ + PyErr_SetString(PyExc_TypeError, "{name}() takes no arguments"); + %s }} - """) + """ % return_error, indent=4)] + + if default_return_converter: + parser_definition = '\n'.join([ + parser_prototype, + '{{', + *parser_code, + ' return {c_basename}_impl({impl_arguments});', + '}}']) else: - parser_definition = parser_body(parser_prototype) + parser_definition = parser_body(parser_prototype, *parser_code) elif meth_o: flags = "METH_O" @@ -939,6 +955,9 @@ def parser_body(prototype, *fields, declarations=''): add_label = None for i, p in enumerate(parameters): + if isinstance(p.converter, defining_class_converter): + raise ValueError("defining_class should be the first " + "parameter (after self)") displayname = p.get_displayname(i+1) parsearg = p.converter.parse_arg(argname_fmt % i, displayname) if parsearg is None: