From 9f4ae13d2a432faa80e76e3948aec43f856fd320 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Thu, 18 Apr 2024 06:11:17 +0900 Subject: [3.12] gh-117613: Argument Clinic: ensure that defining class params are positional-only (#117939) --- Lib/test/test_clinic.py | 22 ++++++++++++++++++++++ Tools/clinic/clinic.py | 8 +++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 3a0ff94..c114a62 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -1347,6 +1347,28 @@ class ClinicParserTest(_ParserBase): parser_decl = p.simple_declaration(in_parser=True) self.assertNotIn("Py_UNUSED", parser_decl) + def test_kind_defining_class(self): + function = self.parse_function(""" + module m + class m.C "PyObject *" "" + m.C.meth + cls: defining_class + """, signatures_in_block=3, function_index=2) + p = function.parameters['cls'] + self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY) + + def test_disallow_defining_class_at_module_level(self): + expected_error_msg = ( + "Error on line 0:\n" + "A 'defining_class' parameter cannot be defined at module level.\n" + ) + out = self.parse_function_should_fail(""" + module m + m.func + cls: defining_class + """) + self.assertEqual(out, expected_error_msg) + def parse(self, text): c = FakeClinic() parser = DSLParser(c) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index d2ff422..efd519f 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -4997,6 +4997,9 @@ class DSLParser: fail("A 'defining_class' parameter cannot have a default value.") if self.group: fail("A 'defining_class' parameter cannot be in an optional group.") + if self.function.cls is None: + fail("A 'defining_class' parameter cannot be defined at module level.") + kind = inspect.Parameter.POSITIONAL_ONLY else: fail("A 'defining_class' parameter, if specified, must either be the first thing in the parameter block, or come just after 'self'.") @@ -5074,7 +5077,10 @@ class DSLParser: for p in self.function.parameters.values(): if p.is_vararg(): continue - if (p.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD and not isinstance(p.converter, self_converter)): + if (p.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD and + not isinstance(p.converter, self_converter) and + not isinstance(p.converter, defining_class_converter) + ): fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.") p.kind = inspect.Parameter.POSITIONAL_ONLY -- cgit v0.12