From 970e1147bfcade58a2132172af629a80299bf3dd Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 1 Mar 2024 01:13:00 +0100 Subject: [PATCH 1/5] Move param guard to param state machine --- Lib/test/test_clinic.py | 2 +- Tools/clinic/clinic.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index f5e9b11ad1cc8a..fbc2e1baf488c8 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -2167,7 +2167,7 @@ class Foo "" "" obj: int / """ - expected_error = f"{annotation} method cannot define parameters" + expected_error = f"{annotation} methods cannot define parameters" self.expect_failure(block, expected_error) def test_setter_docstring(self): diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 7906e7c95d17ba..4e0b96bfd213fd 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -859,9 +859,6 @@ def parser_body( limited_capi = False parsearg: str | None - if f.kind in {GETTER, SETTER} and parameters: - fail(f"@{f.kind.name.lower()} method cannot define parameters") - if not parameters: parser_code: list[str] | None if f.kind is GETTER: @@ -5292,6 +5289,10 @@ def state_parameters_start(self, line: str) -> None: if not self.indent.infer(line): return self.next(self.state_function_docstring, line) + if self.function.kind in {GETTER, SETTER}: + getset = self.function.kind.name.lower() + fail(f"@{getset} methods cannot define parameters") + self.parameter_continuation = '' return self.next(self.state_parameter, line) From adbe7cb19b3640384a11972e696a44154879aa89 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 1 Mar 2024 01:47:02 +0100 Subject: [PATCH 2/5] Override return converter during parsing --- Lib/test/clinic.test.c | 8 ++++++-- Lib/test/test_clinic.py | 4 ++-- Tools/clinic/clinic.py | 23 +++++++---------------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 168f6f73f6186f..7800ae30244544 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -5004,12 +5004,16 @@ Test_property_set_impl(TestObj *self, PyObject *value); static int Test_property_set(TestObj *self, PyObject *value, void *Py_UNUSED(context)) { - return Test_property_set_impl(self, value); + int return_value; + + return_value = Test_property_set_impl(self, value); + + return return_value; } static int Test_property_set_impl(TestObj *self, PyObject *value) -/*[clinic end generated code: output=9797cd03c5204ddb input=3bc3f46a23c83a88]*/ +/*[clinic end generated code: output=d51023f17c4ac3a1 input=3bc3f46a23c83a88]*/ /*[clinic input] output push diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index fbc2e1baf488c8..8a58de1710ae17 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -2647,11 +2647,11 @@ def test_cli_converters(self): bool() double() float() - init() int() long() Py_ssize_t() size_t() + type_slot_int() unsigned_int() unsigned_long() @@ -3937,7 +3937,7 @@ def test_Function_and_Parameter_reprs(self): cls=None, c_basename=None, full_name='foofoo', - return_converter=clinic.init_return_converter(), + return_converter=clinic.type_slot_int_return_converter(), kind=clinic.FunctionKind.METHOD_INIT, coexist=False ) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 4e0b96bfd213fd..4644d569733cd6 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1613,11 +1613,7 @@ def render_function( converter.set_template_dict(template_dict) f.return_converter.render(f, data) - if f.kind is SETTER: - # All setters return an int. - template_dict['impl_return_type'] = 'int' - else: - template_dict['impl_return_type'] = f.return_converter.type + template_dict['impl_return_type'] = f.return_converter.type template_dict['declarations'] = libclinic.format_escape("\n".join(data.declarations)) template_dict['initializers'] = "\n\n".join(data.initializers) @@ -4562,18 +4558,13 @@ class int_return_converter(long_return_converter): cast = '(long)' -class init_return_converter(long_return_converter): - """ - Special return converter for __init__ functions. - """ +class type_slot_int_return_converter(long_return_converter): + """Special return converter for type slots functions that return int.""" type = 'int' cast = '(long)' - def render( - self, - function: Function, - data: CRenderData - ) -> None: ... + def render(self, function: Function, data: CRenderData) -> None: + pass class unsigned_long_return_converter(long_return_converter): @@ -5106,8 +5097,8 @@ def resolve_return_converter( except ValueError: fail(f"Badly formed annotation for {full_name!r}: {forced_converter!r}") - if self.kind is METHOD_INIT: - return init_return_converter() + if self.kind in {METHOD_INIT, SETTER}: + return type_slot_int_return_converter() return CReturnConverter() def parse_cloned_function(self, names: FunctionNames, existing: str) -> None: From 972d0dbd3662e0414af3677f77aaa752b93a34dc Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 1 Mar 2024 02:14:01 +0100 Subject: [PATCH 3/5] Make mypy happy --- Tools/clinic/clinic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 4644d569733cd6..d5768604f3f3cb 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -5280,6 +5280,7 @@ def state_parameters_start(self, line: str) -> None: if not self.indent.infer(line): return self.next(self.state_function_docstring, line) + assert self.function if self.function.kind in {GETTER, SETTER}: getset = self.function.kind.name.lower() fail(f"@{getset} methods cannot define parameters") From cde8a6d4b66a5028cfaa7c2dae6b67c68c2ba5bc Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 1 Mar 2024 02:31:30 +0100 Subject: [PATCH 4/5] Don't use a custom type slot return converter; instead special case type slot functions during generation --- Lib/test/clinic.test.c | 9 ++------- Lib/test/test_clinic.py | 3 +-- Tools/clinic/clinic.py | 14 +++----------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c index 7800ae30244544..b42c998af0f980 100644 --- a/Lib/test/clinic.test.c +++ b/Lib/test/clinic.test.c @@ -5350,7 +5350,6 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; PyTypeObject *base_tp = TestType; - long _return_value; if ((Py_IS_TYPE(self, base_tp) || Py_TYPE(self)->tp_new == base_tp->tp_new) && @@ -5362,11 +5361,7 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) !_PyArg_NoKeywords("Test", kwargs)) { goto exit; } - _return_value = Test___init___impl((TestObj *)self); - if ((_return_value == -1) && PyErr_Occurred()) { - goto exit; - } - return_value = PyLong_FromLong(_return_value); + return_value = Test___init___impl((TestObj *)self); exit: return return_value; @@ -5374,7 +5369,7 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs) static long Test___init___impl(TestObj *self) -/*[clinic end generated code: output=daf6ee12c4e443fb input=311af0dc7f17e8e9]*/ +/*[clinic end generated code: output=9f3704989ab1f6eb input=311af0dc7f17e8e9]*/ /*[clinic input] diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 8a58de1710ae17..2c6b0da33bcaec 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -2651,7 +2651,6 @@ def test_cli_converters(self): long() Py_ssize_t() size_t() - type_slot_int() unsigned_int() unsigned_long() @@ -3937,7 +3936,7 @@ def test_Function_and_Parameter_reprs(self): cls=None, c_basename=None, full_name='foofoo', - return_converter=clinic.type_slot_int_return_converter(), + return_converter=clinic.int_return_converter(), kind=clinic.FunctionKind.METHOD_INIT, coexist=False ) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index d5768604f3f3cb..d133f4e3342dd9 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1612,7 +1612,8 @@ def render_function( for converter in converters: converter.set_template_dict(template_dict) - f.return_converter.render(f, data) + if f.kind not in {SETTER, METHOD_INIT}: + f.return_converter.render(f, data) template_dict['impl_return_type'] = f.return_converter.type template_dict['declarations'] = libclinic.format_escape("\n".join(data.declarations)) @@ -4558,15 +4559,6 @@ class int_return_converter(long_return_converter): cast = '(long)' -class type_slot_int_return_converter(long_return_converter): - """Special return converter for type slots functions that return int.""" - type = 'int' - cast = '(long)' - - def render(self, function: Function, data: CRenderData) -> None: - pass - - class unsigned_long_return_converter(long_return_converter): type = 'unsigned long' conversion_fn = 'PyLong_FromUnsignedLong' @@ -5098,7 +5090,7 @@ def resolve_return_converter( fail(f"Badly formed annotation for {full_name!r}: {forced_converter!r}") if self.kind in {METHOD_INIT, SETTER}: - return type_slot_int_return_converter() + return int_return_converter() return CReturnConverter() def parse_cloned_function(self, names: FunctionNames, existing: str) -> None: From cecb56aa15cc47afaccb35ef3c5b29f32ea11eb1 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 1 Mar 2024 02:50:25 +0100 Subject: [PATCH 5/5] Update Tools/clinic/clinic.py --- 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 d133f4e3342dd9..e9465e5a3b2ea3 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -5272,7 +5272,7 @@ def state_parameters_start(self, line: str) -> None: if not self.indent.infer(line): return self.next(self.state_function_docstring, line) - assert self.function + assert self.function is not None if self.function.kind in {GETTER, SETTER}: getset = self.function.kind.name.lower() fail(f"@{getset} methods cannot define parameters")