| | |
| |
|
| | import re |
| | import sys |
| | import typing |
| |
|
| | from .util import col, line, lineno, _collapse_string_to_ranges |
| | from .unicode import pyparsing_unicode as ppu |
| |
|
| |
|
| | class ExceptionWordUnicode(ppu.Latin1, ppu.LatinA, ppu.LatinB, ppu.Greek, ppu.Cyrillic): |
| | pass |
| |
|
| |
|
| | _extract_alphanums = _collapse_string_to_ranges(ExceptionWordUnicode.alphanums) |
| | _exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.") |
| |
|
| |
|
| | class ParseBaseException(Exception): |
| | """base exception class for all parsing runtime exceptions""" |
| |
|
| | |
| | |
| | def __init__( |
| | self, |
| | pstr: str, |
| | loc: int = 0, |
| | msg: typing.Optional[str] = None, |
| | elem=None, |
| | ): |
| | self.loc = loc |
| | if msg is None: |
| | self.msg = pstr |
| | self.pstr = "" |
| | else: |
| | self.msg = msg |
| | self.pstr = pstr |
| | self.parser_element = self.parserElement = elem |
| | self.args = (pstr, loc, msg) |
| |
|
| | @staticmethod |
| | def explain_exception(exc, depth=16): |
| | """ |
| | Method to take an exception and translate the Python internal traceback into a list |
| | of the pyparsing expressions that caused the exception to be raised. |
| | |
| | Parameters: |
| | |
| | - exc - exception raised during parsing (need not be a ParseException, in support |
| | of Python exceptions that might be raised in a parse action) |
| | - depth (default=16) - number of levels back in the stack trace to list expression |
| | and function names; if None, the full stack trace names will be listed; if 0, only |
| | the failing input line, marker, and exception string will be shown |
| | |
| | Returns a multi-line string listing the ParserElements and/or function names in the |
| | exception's stack trace. |
| | """ |
| | import inspect |
| | from .core import ParserElement |
| |
|
| | if depth is None: |
| | depth = sys.getrecursionlimit() |
| | ret = [] |
| | if isinstance(exc, ParseBaseException): |
| | ret.append(exc.line) |
| | ret.append(" " * (exc.column - 1) + "^") |
| | ret.append("{}: {}".format(type(exc).__name__, exc)) |
| |
|
| | if depth > 0: |
| | callers = inspect.getinnerframes(exc.__traceback__, context=depth) |
| | seen = set() |
| | for i, ff in enumerate(callers[-depth:]): |
| | frm = ff[0] |
| |
|
| | f_self = frm.f_locals.get("self", None) |
| | if isinstance(f_self, ParserElement): |
| | if frm.f_code.co_name not in ("parseImpl", "_parseNoCache"): |
| | continue |
| | if id(f_self) in seen: |
| | continue |
| | seen.add(id(f_self)) |
| |
|
| | self_type = type(f_self) |
| | ret.append( |
| | "{}.{} - {}".format( |
| | self_type.__module__, self_type.__name__, f_self |
| | ) |
| | ) |
| |
|
| | elif f_self is not None: |
| | self_type = type(f_self) |
| | ret.append("{}.{}".format(self_type.__module__, self_type.__name__)) |
| |
|
| | else: |
| | code = frm.f_code |
| | if code.co_name in ("wrapper", "<module>"): |
| | continue |
| |
|
| | ret.append("{}".format(code.co_name)) |
| |
|
| | depth -= 1 |
| | if not depth: |
| | break |
| |
|
| | return "\n".join(ret) |
| |
|
| | @classmethod |
| | def _from_exception(cls, pe): |
| | """ |
| | internal factory method to simplify creating one type of ParseException |
| | from another - avoids having __init__ signature conflicts among subclasses |
| | """ |
| | return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) |
| |
|
| | @property |
| | def line(self) -> str: |
| | """ |
| | Return the line of text where the exception occurred. |
| | """ |
| | return line(self.loc, self.pstr) |
| |
|
| | @property |
| | def lineno(self) -> int: |
| | """ |
| | Return the 1-based line number of text where the exception occurred. |
| | """ |
| | return lineno(self.loc, self.pstr) |
| |
|
| | @property |
| | def col(self) -> int: |
| | """ |
| | Return the 1-based column on the line of text where the exception occurred. |
| | """ |
| | return col(self.loc, self.pstr) |
| |
|
| | @property |
| | def column(self) -> int: |
| | """ |
| | Return the 1-based column on the line of text where the exception occurred. |
| | """ |
| | return col(self.loc, self.pstr) |
| |
|
| | def __str__(self) -> str: |
| | if self.pstr: |
| | if self.loc >= len(self.pstr): |
| | foundstr = ", found end of text" |
| | else: |
| | |
| | found_match = _exception_word_extractor.match(self.pstr, self.loc) |
| | if found_match is not None: |
| | found = found_match.group(0) |
| | else: |
| | found = self.pstr[self.loc : self.loc + 1] |
| | foundstr = (", found %r" % found).replace(r"\\", "\\") |
| | else: |
| | foundstr = "" |
| | return "{}{} (at char {}), (line:{}, col:{})".format( |
| | self.msg, foundstr, self.loc, self.lineno, self.column |
| | ) |
| |
|
| | def __repr__(self): |
| | return str(self) |
| |
|
| | def mark_input_line(self, marker_string: str = None, *, markerString=">!<") -> str: |
| | """ |
| | Extracts the exception line from the input string, and marks |
| | the location of the exception with a special symbol. |
| | """ |
| | markerString = marker_string if marker_string is not None else markerString |
| | line_str = self.line |
| | line_column = self.column - 1 |
| | if markerString: |
| | line_str = "".join( |
| | (line_str[:line_column], markerString, line_str[line_column:]) |
| | ) |
| | return line_str.strip() |
| |
|
| | def explain(self, depth=16) -> str: |
| | """ |
| | Method to translate the Python internal traceback into a list |
| | of the pyparsing expressions that caused the exception to be raised. |
| | |
| | Parameters: |
| | |
| | - depth (default=16) - number of levels back in the stack trace to list expression |
| | and function names; if None, the full stack trace names will be listed; if 0, only |
| | the failing input line, marker, and exception string will be shown |
| | |
| | Returns a multi-line string listing the ParserElements and/or function names in the |
| | exception's stack trace. |
| | |
| | Example:: |
| | |
| | expr = pp.Word(pp.nums) * 3 |
| | try: |
| | expr.parse_string("123 456 A789") |
| | except pp.ParseException as pe: |
| | print(pe.explain(depth=0)) |
| | |
| | prints:: |
| | |
| | 123 456 A789 |
| | ^ |
| | ParseException: Expected W:(0-9), found 'A' (at char 8), (line:1, col:9) |
| | |
| | Note: the diagnostic output will include string representations of the expressions |
| | that failed to parse. These representations will be more helpful if you use `set_name` to |
| | give identifiable names to your expressions. Otherwise they will use the default string |
| | forms, which may be cryptic to read. |
| | |
| | Note: pyparsing's default truncation of exception tracebacks may also truncate the |
| | stack of expressions that are displayed in the ``explain`` output. To get the full listing |
| | of parser expressions, you may have to set ``ParserElement.verbose_stacktrace = True`` |
| | """ |
| | return self.explain_exception(self, depth) |
| |
|
| | markInputline = mark_input_line |
| |
|
| |
|
| | class ParseException(ParseBaseException): |
| | """ |
| | Exception thrown when a parse expression doesn't match the input string |
| | |
| | Example:: |
| | |
| | try: |
| | Word(nums).set_name("integer").parse_string("ABC") |
| | except ParseException as pe: |
| | print(pe) |
| | print("column: {}".format(pe.column)) |
| | |
| | prints:: |
| | |
| | Expected integer (at char 0), (line:1, col:1) |
| | column: 1 |
| | |
| | """ |
| |
|
| |
|
| | class ParseFatalException(ParseBaseException): |
| | """ |
| | User-throwable exception thrown when inconsistent parse content |
| | is found; stops all parsing immediately |
| | """ |
| |
|
| |
|
| | class ParseSyntaxException(ParseFatalException): |
| | """ |
| | Just like :class:`ParseFatalException`, but thrown internally |
| | when an :class:`ErrorStop<And._ErrorStop>` ('-' operator) indicates |
| | that parsing is to stop immediately because an unbacktrackable |
| | syntax error has been found. |
| | """ |
| |
|
| |
|
| | class RecursiveGrammarException(Exception): |
| | """ |
| | Exception thrown by :class:`ParserElement.validate` if the |
| | grammar could be left-recursive; parser may need to enable |
| | left recursion using :class:`ParserElement.enable_left_recursion<ParserElement.enable_left_recursion>` |
| | """ |
| |
|
| | def __init__(self, parseElementList): |
| | self.parseElementTrace = parseElementList |
| |
|
| | def __str__(self) -> str: |
| | return "RecursiveGrammarException: {}".format(self.parseElementTrace) |
| |
|