summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErlend E. Aasland <erlend@python.org>2023-12-27 21:43:19 (GMT)
committerGitHub <noreply@github.com>2023-12-27 21:43:19 (GMT)
commit87295b4068762f9cbdfcae5fed5ff54aadd3cb62 (patch)
tree8457448ab9f4cf1cb2795ad8dff6f48768fb3182
parent6c98fce33a4c2d6671978f6286377af0d6e22182 (diff)
downloadcpython-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.py4
-rwxr-xr-xTools/clinic/clinic.py23
-rw-r--r--Tools/clinic/cpp.py21
-rw-r--r--Tools/clinic/libclinic/__init__.py6
-rw-r--r--Tools/clinic/libclinic/errors.py26
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