From 607f18c89456cdc9064e27f86a7505e011209757 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 15 Aug 2023 16:41:40 +0200 Subject: gh-107972: Argument Clinic: Ensure a C basename is provided after 'as' (#107973) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- Lib/test/test_clinic.py | 30 ++++++++++++++++++++++++++---- Tools/clinic/clinic.py | 9 ++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index a67cd30..8967305 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -28,7 +28,8 @@ def _make_clinic(*, filename='clinic_tests'): return c -def _expect_failure(tc, parser, code, errmsg, *, filename=None, lineno=None): +def _expect_failure(tc, parser, code, errmsg, *, filename=None, lineno=None, + strip=True): """Helper for the parser tests. tc: unittest.TestCase; passed self in the wrapper @@ -38,7 +39,9 @@ def _expect_failure(tc, parser, code, errmsg, *, filename=None, lineno=None): filename: str, optional filename lineno: int, optional line number """ - code = dedent(code).strip() + code = dedent(code) + if strip: + code = code.strip() errmsg = re.escape(errmsg) with tc.assertRaisesRegex(clinic.ClinicError, errmsg) as cm: parser(code) @@ -638,6 +641,19 @@ class ClinicWholeFileTest(TestCase): err = "'__new__' must be a class method" self.expect_failure(block, err, lineno=7) + def test_no_c_basename_cloned(self): + block = """ + /*[clinic input] + foo2 + [clinic start generated code]*/ + /*[clinic input] + foo as = foo2 + [clinic start generated code]*/ + """ + err = "No C basename provided after 'as' keyword" + self.expect_failure(block, err, lineno=5) + + class ParseFileUnitTest(TestCase): def expect_parsing_failure( @@ -857,9 +873,10 @@ class ClinicParserTest(TestCase): assert isinstance(s[function_index], clinic.Function) return s[function_index] - def expect_failure(self, block, err, *, filename=None, lineno=None): + def expect_failure(self, block, err, *, + filename=None, lineno=None, strip=True): return _expect_failure(self, self.parse_function, block, err, - filename=filename, lineno=lineno) + filename=filename, lineno=lineno, strip=strip) def checkDocstring(self, fn, expected): self.assertTrue(hasattr(fn, "docstring")) @@ -1520,6 +1537,11 @@ class ClinicParserTest(TestCase): err = "Illegal C basename: '935'" self.expect_failure(block, err) + def test_no_c_basename(self): + block = "foo as " + err = "No C basename provided after 'as' keyword" + self.expect_failure(block, err, strip=False) + def test_single_star(self): block = """ module foo diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 11dbfb3..6ff2622 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -4879,9 +4879,11 @@ class DSLParser: before, equals, existing = line.rpartition('=') c_basename: str | None if equals: - full_name, _, c_basename = before.partition(' as ') + full_name, as_, c_basename = before.partition(' as ') full_name = full_name.strip() c_basename = c_basename.strip() + if as_ and not c_basename: + fail("No C basename provided after 'as' keyword") existing = existing.strip() if (is_legal_py_identifier(full_name) and (not c_basename or is_legal_c_identifier(c_basename)) and @@ -4932,10 +4934,11 @@ class DSLParser: line, _, returns = line.partition('->') returns = returns.strip() - full_name, _, c_basename = line.partition(' as ') + full_name, as_, c_basename = line.partition(' as ') full_name = full_name.strip() c_basename = c_basename.strip() or None - + if as_ and not c_basename: + fail("No C basename provided after 'as' keyword") if not is_legal_py_identifier(full_name): fail(f"Illegal function name: {full_name!r}") if c_basename and not is_legal_c_identifier(c_basename): -- cgit v0.12