summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2022-05-03 08:54:06 (GMT)
committerGitHub <noreply@github.com>2022-05-03 08:54:06 (GMT)
commit1b1c79c566b8aa527f551cb4d41d3fab09b781a5 (patch)
tree6a208eb3df821a594246c8ab792bcee3383cc9a4
parent7e55730e3f0a0c8c1b73037011e4b00dd7bd7f69 (diff)
downloadcpython-1b1c79c566b8aa527f551cb4d41d3fab09b781a5.zip
cpython-1b1c79c566b8aa527f551cb4d41d3fab09b781a5.tar.gz
cpython-1b1c79c566b8aa527f551cb4d41d3fab09b781a5.tar.bz2
[3.9] gh-91583: AC: Fix regression for functions with defining_class (GH-91739) (GH-92080)
Argument Clinic now generates the same efficient code as before adding the defining_class parameter. (cherry picked from commit a055dac0b45031878a8196a8735522de018491e3)
-rw-r--r--Misc/NEWS.d/next/Tools-Demos/2022-04-20-14-26-14.gh-issue-91583.200qI0.rst2
-rw-r--r--Modules/clinic/_testmultiphase.c.h66
-rw-r--r--Modules/clinic/posixmodule.c.h56
-rwxr-xr-xTools/clinic/clinic.py53
4 files changed, 120 insertions, 57 deletions
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 0000000..bdfa711
--- /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 0d38c23..23180d0 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 28798ab..f5df5c3 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 @@ exit:
#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 61d30b0..655e386 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -636,6 +636,10 @@ class CLanguage(Language):
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 @@ class CLanguage(Language):
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 @@ class CLanguage(Language):
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 @@ class CLanguage(Language):
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: