diff --git a/pyparsing/exceptions.py b/pyparsing/exceptions.py index 5324abfa..7775ed37 100644 --- a/pyparsing/exceptions.py +++ b/pyparsing/exceptions.py @@ -5,15 +5,16 @@ import re import sys import typing +from functools import cached_property +from .unicode import pyparsing_unicode as ppu from .util import ( + _collapse_string_to_ranges, col, line, lineno, - _collapse_string_to_ranges, replaced_by_pep8, ) -from .unicode import pyparsing_unicode as ppu class _ExceptionWordUnicodeSet( @@ -134,34 +135,51 @@ def _from_exception(cls, pe) -> ParseBaseException: """ return cls(pe.pstr, pe.loc, pe.msg, pe.parser_element) - @property + @cached_property def line(self) -> str: """ Return the line of text where the exception occurred. """ return line(self.loc, self.pstr) - @property + @cached_property def lineno(self) -> int: """ Return the 1-based line number of text where the exception occurred. """ return lineno(self.loc, self.pstr) - @property + @cached_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 + @cached_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) + @cached_property + def found(self) -> str: + if not self.pstr: + return "" + + if self.loc >= len(self.pstr): + return "end of text" + + # pull out next word at error location + 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] + + return repr(found).replace(r"\\", "\\") + # pre-PEP8 compatibility @property def parserElement(self): @@ -174,21 +192,12 @@ def parserElement(self, elem): def copy(self): return copy.copy(self) + def formatted_message(self): + found_phrase = f", found {self.found}" if self.found else "" + return f"{self.msg}{found_phrase} (at char {self.loc}), (line:{self.lineno}, col:{self.column})" + def __str__(self) -> str: - if self.pstr: - if self.loc >= len(self.pstr): - foundstr = ", found end of text" - else: - # pull out next word at error location - 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 f"{self.msg}{foundstr} (at char {self.loc}), (line:{self.lineno}, col:{self.column})" + return self.formatted_message() def __repr__(self): return str(self)