diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index a67cd301f6eaa88..896730571d81522 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 C "void *" "" 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 @@ def parse_function(self, text, signatures_in_block=2, function_index=1): 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 @@ def test_illegal_c_basename(self): 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 11dbfb3fbe858ec..6ff2622d33b381a 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -4879,9 +4879,11 @@ def state_modulename_name(self, line: str) -> None: 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 @@ def state_modulename_name(self, line: str) -> None: 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):