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
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
# Local imports.
import libclinic
+from libclinic import ClinicError
# TODO:
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,
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)
import sys
from typing import NoReturn
+from libclinic.errors import ParseError
+
TokenAndCondition = tuple[str, str]
TokenStack = list[TokenAndCondition]
Anyway this implementation seems to work well enough for the CPython sources.
"""
- filename: str | None = None
+ filename: str
_: dc.KW_ONLY
verbose: bool = False
"""
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
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:
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 + ")"
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':
from typing import Final
+from .errors import (
+ ClinicError,
+)
from .formatting import (
SIG_END_MARKER,
c_repr,
__all__ = [
+ # Error handling
+ "ClinicError",
+
# Formatting helpers
"SIG_END_MARKER",
"c_repr",
--- /dev/null
+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