diff options
author | Erlend E. Aasland <erlend@python.org> | 2023-12-27 21:43:19 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 21:43:19 (GMT) |
commit | 87295b4068762f9cbdfcae5fed5ff54aadd3cb62 (patch) | |
tree | 8457448ab9f4cf1cb2795ad8dff6f48768fb3182 | |
parent | 6c98fce33a4c2d6671978f6286377af0d6e22182 (diff) | |
download | cpython-87295b4068762f9cbdfcae5fed5ff54aadd3cb62.zip cpython-87295b4068762f9cbdfcae5fed5ff54aadd3cb62.tar.gz cpython-87295b4068762f9cbdfcae5fed5ff54aadd3cb62.tar.bz2 |
gh-113317: Rework Argument Clinic cpp.py error handling (#113525)
Rework error handling in the C preprocessor helper. Instead of monkey-
patching the cpp.Monitor.fail() method from within clinic.py, rewrite
cpp.py to use a subclass of the ClinicError exception. As a side-effect,
ClinicError is moved into Tools/clinic/libclinic/errors.py.
Yak-shaving in preparation for putting cpp.py into libclinic.
-rw-r--r-- | Lib/test/test_clinic.py | 4 | ||||
-rwxr-xr-x | Tools/clinic/clinic.py | 23 | ||||
-rw-r--r-- | Tools/clinic/cpp.py | 21 | ||||
-rw-r--r-- | Tools/clinic/libclinic/__init__.py | 6 | ||||
-rw-r--r-- | Tools/clinic/libclinic/errors.py | 26 |
5 files changed, 44 insertions, 36 deletions
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 21f56fe..3d6816d 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -22,7 +22,7 @@ with test_tools.imports_under_tool('clinic'): def _make_clinic(*, filename='clinic_tests'): - clang = clinic.CLanguage(None) + clang = clinic.CLanguage(filename) c = clinic.Clinic(clang, filename=filename, limited_capi=False) c.block_parser = clinic.BlockParser('', clang) return c @@ -3920,7 +3920,7 @@ class ClinicReprTests(unittest.TestCase): self.assertEqual(repr(parameter), "<clinic.Parameter 'bar'>") def test_Monitor_repr(self): - monitor = clinic.cpp.Monitor() + monitor = clinic.cpp.Monitor("test.c") self.assertRegex(repr(monitor), r"<clinic.Monitor \d+ line=0 condition=''>") monitor.line_number = 42 diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index f004bec3..82efff5 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -53,6 +53,7 @@ from typing import ( # Local imports. import libclinic +from libclinic import ClinicError # TODO: @@ -94,27 +95,6 @@ NULL = Null() TemplateDict = dict[str, str] -@dc.dataclass -class ClinicError(Exception): - message: str - _: dc.KW_ONLY - lineno: int | None = None - filename: str | None = None - - def __post_init__(self) -> None: - super().__init__(self.message) - - def report(self, *, warn_only: bool = False) -> str: - msg = "Warning" if warn_only else "Error" - if self.filename is not None: - msg += f" in file {self.filename!r}" - if self.lineno is not None: - msg += f" on line {self.lineno}" - msg += ":\n" - msg += f"{self.message}\n" - return msg - - @overload def warn_or_fail( *args: object, @@ -669,7 +649,6 @@ class CLanguage(Language): def __init__(self, filename: str) -> None: super().__init__(filename) self.cpp = cpp.Monitor(filename) - self.cpp.fail = fail # type: ignore[method-assign] def parse_line(self, line: str) -> None: self.cpp.writeline(line) diff --git a/Tools/clinic/cpp.py b/Tools/clinic/cpp.py index 16eee6f..6590990 100644 --- a/Tools/clinic/cpp.py +++ b/Tools/clinic/cpp.py @@ -3,6 +3,8 @@ import re import sys from typing import NoReturn +from libclinic.errors import ParseError + TokenAndCondition = tuple[str, str] TokenStack = list[TokenAndCondition] @@ -32,7 +34,7 @@ class Monitor: Anyway this implementation seems to work well enough for the CPython sources. """ - filename: str | None = None + filename: str _: dc.KW_ONLY verbose: bool = False @@ -59,14 +61,8 @@ class Monitor: """ return " && ".join(condition for token, condition in self.stack) - def fail(self, *a: object) -> NoReturn: - if self.filename: - filename = " " + self.filename - else: - filename = '' - print("Error at" + filename, "line", self.line_number, ":") - print(" ", ' '.join(str(x) for x in a)) - sys.exit(-1) + def fail(self, msg: str) -> NoReturn: + raise ParseError(msg, filename=self.filename, lineno=self.line_number) def writeline(self, line: str) -> None: self.line_number += 1 @@ -74,7 +70,7 @@ class Monitor: def pop_stack() -> TokenAndCondition: if not self.stack: - self.fail("#" + token + " without matching #if / #ifdef / #ifndef!") + self.fail(f"#{token} without matching #if / #ifdef / #ifndef!") return self.stack.pop() if self.continuation: @@ -145,7 +141,7 @@ class Monitor: if token in {'if', 'ifdef', 'ifndef', 'elif'}: if not condition: - self.fail("Invalid format for #" + token + " line: no argument!") + self.fail(f"Invalid format for #{token} line: no argument!") if token in {'if', 'elif'}: if not is_a_simple_defined(condition): condition = "(" + condition + ")" @@ -155,7 +151,8 @@ class Monitor: else: fields = condition.split() if len(fields) != 1: - self.fail("Invalid format for #" + token + " line: should be exactly one argument!") + self.fail(f"Invalid format for #{token} line: " + "should be exactly one argument!") symbol = fields[0] condition = 'defined(' + symbol + ')' if token == 'ifndef': diff --git a/Tools/clinic/libclinic/__init__.py b/Tools/clinic/libclinic/__init__.py index 0c3c684..d4e7a0c 100644 --- a/Tools/clinic/libclinic/__init__.py +++ b/Tools/clinic/libclinic/__init__.py @@ -1,5 +1,8 @@ from typing import Final +from .errors import ( + ClinicError, +) from .formatting import ( SIG_END_MARKER, c_repr, @@ -15,6 +18,9 @@ from .formatting import ( __all__ = [ + # Error handling + "ClinicError", + # Formatting helpers "SIG_END_MARKER", "c_repr", diff --git a/Tools/clinic/libclinic/errors.py b/Tools/clinic/libclinic/errors.py new file mode 100644 index 0000000..afb21b0 --- /dev/null +++ b/Tools/clinic/libclinic/errors.py @@ -0,0 +1,26 @@ +import dataclasses as dc + + +@dc.dataclass +class ClinicError(Exception): + message: str + _: dc.KW_ONLY + lineno: int | None = None + filename: str | None = None + + def __post_init__(self) -> None: + super().__init__(self.message) + + def report(self, *, warn_only: bool = False) -> str: + msg = "Warning" if warn_only else "Error" + if self.filename is not None: + msg += f" in file {self.filename!r}" + if self.lineno is not None: + msg += f" on line {self.lineno}" + msg += ":\n" + msg += f"{self.message}\n" + return msg + + +class ParseError(ClinicError): + pass |