From 9b4a1f7f5680fc6cf437c8c91e0eb2e9a6d06947 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 19 Dec 2023 01:32:48 +0100 Subject: [PATCH 1/4] gh-112205: Require @getter and @setter to be class methods --- Tools/clinic/clinic.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index a9bf110291eadd..5bba5bfe487b7c 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -5614,6 +5614,10 @@ def state_modulename_name(self, line: str) -> None: function_name = fields.pop() module, cls = self.clinic._module_and_class(fields) + if self.kind in {GETTER, SETTER}: + if not cls: + fail("@getter and @setter must be class level functions") + self.update_function_kind(full_name) if self.kind is METHOD_INIT and not return_converter: return_converter = init_return_converter() From 528adc23dff0bed9d538593b8933830c1a061bc5 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 19 Dec 2023 10:43:32 +0100 Subject: [PATCH 2/4] 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 5bba5bfe487b7c..d20c12164d5076 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -5616,7 +5616,7 @@ def state_modulename_name(self, line: str) -> None: if self.kind in {GETTER, SETTER}: if not cls: - fail("@getter and @setter must be class level functions") + fail("@getter and @setter must be class methods") self.update_function_kind(full_name) if self.kind is METHOD_INIT and not return_converter: From e0a3f6afbd86b2e39a57b6f907a4bd57f142579d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 19 Dec 2023 11:32:32 +0100 Subject: [PATCH 3/4] Update Tools/clinic/clinic.py Co-authored-by: Alex Waygood --- 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 d20c12164d5076..87feef1b82ca39 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -5616,7 +5616,7 @@ def state_modulename_name(self, line: str) -> None: if self.kind in {GETTER, SETTER}: if not cls: - fail("@getter and @setter must be class methods") + fail("@getter and @setter must be methods") self.update_function_kind(full_name) if self.kind is METHOD_INIT and not return_converter: From 0aebce8d85e1dc5ca670d8168ea3aacc1ca90da5 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 19 Dec 2023 11:37:41 +0100 Subject: [PATCH 4/4] Add test --- Lib/test/test_clinic.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index d3dbde88dd82a9..6c6bd4e75a0b02 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -2249,6 +2249,17 @@ class Foo "" "" expected_error = "Cannot apply both @getter and @setter to the same function!" self.expect_failure(block, expected_error, lineno=3) + def test_getset_no_class(self): + for annotation in "@getter", "@setter": + with self.subTest(annotation=annotation): + block = f""" + module m + {annotation} + m.func + """ + expected_error = "@getter and @setter must be methods" + self.expect_failure(block, expected_error, lineno=2) + def test_duplicate_coexist(self): err = "Called @coexist twice" block = """