From 2899599f40d678cec22e5f8811d83d23e3256ce4 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 3 Jul 2023 22:16:50 +0200 Subject: [PATCH 1/3] [3.11] gh-106368: Increase Argument Clinic test coverage (#106369) Add tests for 'self' and 'defining_class' converter requirements. (cherry picked from commit 7f4c8121db62a9f72f00f2d9f73381e82f289581) Co-authored-by: Erlend E. Aasland --- Lib/test/test_clinic.py | 111 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 8d4f92fa310c3e..dbac535959d732 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -812,6 +812,117 @@ def test_other_bizarre_things_in_annotations_fail(self): ) self.assertEqual(s, expected_failure_message) + def test_kwarg_splats_disallowed_in_function_call_annotations(self): + expected_error_msg = ( + "Error on line 0:\n" + "Cannot use a kwarg splat in a function-call annotation\n" + ) + dataset = ( + 'module fo\nfo.barbaz\n o: bool(**{None: "bang!"})', + 'module fo\nfo.barbaz -> bool(**{None: "bang!"})', + 'module fo\nfo.barbaz -> bool(**{"bang": 42})', + 'module fo\nfo.barbaz\n o: bool(**{"bang": None})', + ) + for fn in dataset: + with self.subTest(fn=fn): + out = self.parse_function_should_fail(fn) + self.assertEqual(out, expected_error_msg) + + def test_self_param_placement(self): + expected_error_msg = ( + "Error on line 0:\n" + "A 'self' parameter, if specified, must be the very first thing " + "in the parameter block.\n" + ) + block = """ + module foo + foo.func + a: int + self: self(type="PyObject *") + """ + out = self.parse_function_should_fail(block) + self.assertEqual(out, expected_error_msg) + + def test_self_param_cannot_be_optional(self): + expected_error_msg = ( + "Error on line 0:\n" + "A 'self' parameter cannot be marked optional.\n" + ) + block = """ + module foo + foo.func + self: self(type="PyObject *") = None + """ + out = self.parse_function_should_fail(block) + self.assertEqual(out, expected_error_msg) + + def test_defining_class_param_placement(self): + expected_error_msg = ( + "Error on line 0:\n" + "A 'defining_class' parameter, if specified, must either be the " + "first thing in the parameter block, or come just after 'self'.\n" + ) + block = """ + module foo + foo.func + self: self(type="PyObject *") + a: int + cls: defining_class + """ + out = self.parse_function_should_fail(block) + self.assertEqual(out, expected_error_msg) + + def test_defining_class_param_cannot_be_optional(self): + expected_error_msg = ( + "Error on line 0:\n" + "A 'defining_class' parameter cannot be marked optional.\n" + ) + block = """ + module foo + foo.func + cls: defining_class(type="PyObject *") = None + """ + out = self.parse_function_should_fail(block) + self.assertEqual(out, expected_error_msg) + + def test_unused_param(self): + block = self.parse(""" + module foo + foo.func + fn: object + k: float + i: float(unused=True) + / + * + flag: bool(unused=True) = False + """) + sig = block.signatures[1] # Function index == 1 + params = sig.parameters + conv = lambda fn: params[fn].converter + dataset = ( + {"name": "fn", "unused": False}, + {"name": "k", "unused": False}, + {"name": "i", "unused": True}, + {"name": "flag", "unused": True}, + ) + for param in dataset: + name, unused = param.values() + with self.subTest(name=name, unused=unused): + p = conv(name) + # Verify that the unused flag is parsed correctly. + self.assertEqual(unused, p.unused) + + # Now, check that we'll produce correct code. + decl = p.simple_declaration(in_parser=False) + if unused: + self.assertIn("Py_UNUSED", decl) + else: + self.assertNotIn("Py_UNUSED", decl) + + # Make sure the Py_UNUSED macro is not used in the parser body. + parser_decl = p.simple_declaration(in_parser=True) + self.assertNotIn("Py_UNUSED", parser_decl) + def parse(self, text): c = FakeClinic() parser = DSLParser(c) From b0621092d725ca70db5b69a88234f1395e1c5633 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 3 Jul 2023 23:12:03 +0200 Subject: [PATCH 2/3] Unused param is not available in 3.11 --- Lib/test/test_clinic.py | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index dbac535959d732..be4553a27b245e 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -885,44 +885,6 @@ def test_defining_class_param_cannot_be_optional(self): out = self.parse_function_should_fail(block) self.assertEqual(out, expected_error_msg) - def test_unused_param(self): - block = self.parse(""" - module foo - foo.func - fn: object - k: float - i: float(unused=True) - / - * - flag: bool(unused=True) = False - """) - sig = block.signatures[1] # Function index == 1 - params = sig.parameters - conv = lambda fn: params[fn].converter - dataset = ( - {"name": "fn", "unused": False}, - {"name": "k", "unused": False}, - {"name": "i", "unused": True}, - {"name": "flag", "unused": True}, - ) - for param in dataset: - name, unused = param.values() - with self.subTest(name=name, unused=unused): - p = conv(name) - # Verify that the unused flag is parsed correctly. - self.assertEqual(unused, p.unused) - - # Now, check that we'll produce correct code. - decl = p.simple_declaration(in_parser=False) - if unused: - self.assertIn("Py_UNUSED", decl) - else: - self.assertNotIn("Py_UNUSED", decl) - - # Make sure the Py_UNUSED macro is not used in the parser body. - parser_decl = p.simple_declaration(in_parser=True) - self.assertNotIn("Py_UNUSED", parser_decl) - def parse(self, text): c = FakeClinic() parser = DSLParser(c) From 4ca8a9c53df836ff1a271cd8a8636885518c7681 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 3 Jul 2023 23:36:51 +0200 Subject: [PATCH 3/3] The kwargs bugfix is not in 3.11 --- Lib/test/test_clinic.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index be4553a27b245e..c2d2085ecd6de9 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -812,22 +812,6 @@ def test_other_bizarre_things_in_annotations_fail(self): ) self.assertEqual(s, expected_failure_message) - def test_kwarg_splats_disallowed_in_function_call_annotations(self): - expected_error_msg = ( - "Error on line 0:\n" - "Cannot use a kwarg splat in a function-call annotation\n" - ) - dataset = ( - 'module fo\nfo.barbaz\n o: bool(**{None: "bang!"})', - 'module fo\nfo.barbaz -> bool(**{None: "bang!"})', - 'module fo\nfo.barbaz -> bool(**{"bang": 42})', - 'module fo\nfo.barbaz\n o: bool(**{"bang": None})', - ) - for fn in dataset: - with self.subTest(fn=fn): - out = self.parse_function_should_fail(fn) - self.assertEqual(out, expected_error_msg) - def test_self_param_placement(self): expected_error_msg = ( "Error on line 0:\n"