some better error handling
This commit is contained in:
parent
8ad8366158
commit
75e705c810
3 changed files with 67 additions and 19 deletions
|
@ -1,6 +1,27 @@
|
|||
from typing import Optional
|
||||
|
||||
|
||||
class PyTeXError(Exception):
|
||||
pass
|
||||
def __init__(self, msg, explanation: Optional[str] = None):
|
||||
self._msg = msg
|
||||
self._traceback_explanation = []
|
||||
if explanation is not None:
|
||||
self._traceback_explanation.append(explanation)
|
||||
super().__init__(self._dispstr())
|
||||
|
||||
def _dispstr(self):
|
||||
depth = 0
|
||||
ret = [self._msg]
|
||||
for explanation in self._traceback_explanation:
|
||||
ret.append(' ' * 2 * depth + explanation)
|
||||
depth += 1
|
||||
return '\n'.join(ret)
|
||||
|
||||
def add_explanation(self, explanation: str):
|
||||
self._traceback_explanation.append(explanation)
|
||||
|
||||
def __str__(self):
|
||||
return self._dispstr()
|
||||
|
||||
|
||||
class PyTeXFormattingError(PyTeXError):
|
||||
|
|
|
@ -158,7 +158,9 @@ class MacroCodeBeginMacro(SingleLineMacro):
|
|||
|
||||
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
||||
if not formatter.mode == FormatterMode.meta:
|
||||
raise PyTeXInvalidBeginMacroCodeUsageError
|
||||
raise PyTeXInvalidBeginMacroCodeUsageError(
|
||||
r"\begin{macrocode} used outside meta context"
|
||||
)
|
||||
formatter.mode = FormatterMode.macrocode
|
||||
return r'% \begin{macrocode}'
|
||||
|
||||
|
@ -169,7 +171,9 @@ class MacroCodeEndMacro(SingleLineMacro):
|
|||
|
||||
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
||||
if not formatter.mode == FormatterMode.macrocode:
|
||||
raise PyTeXInvalidEndMacroCodeUsageError
|
||||
raise PyTeXInvalidEndMacroCodeUsageError(
|
||||
r"\end{macrocode} used outside macrocode context"
|
||||
)
|
||||
formatter.mode = FormatterMode.meta
|
||||
return r'% \end{macrocode}'
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ from .macros import Macro
|
|||
from .pytex_formatter import PyTeXFormatter
|
||||
from .enums import *
|
||||
from ..logger import logger
|
||||
from .errors import *
|
||||
|
||||
|
||||
class LineStream:
|
||||
|
@ -15,6 +16,7 @@ class LineStream:
|
|||
self._handle = open(filename, 'r')
|
||||
self._cached_lines: List[str] = []
|
||||
self._file_exhausted: bool = False
|
||||
self._line_number: int = 0
|
||||
|
||||
def current_line(self):
|
||||
return self.future_line(0)
|
||||
|
@ -47,6 +49,7 @@ class LineStream:
|
|||
self._cached_lines.append(
|
||||
self._handle.readline()
|
||||
)
|
||||
self._line_number += 1
|
||||
if self._cached_lines[-1] == '':
|
||||
self._handle.close()
|
||||
self._file_exhausted = True
|
||||
|
@ -57,6 +60,10 @@ class LineStream:
|
|||
b = len(self._cached_lines) == 0
|
||||
return a & b
|
||||
|
||||
@property
|
||||
def line_number(self) -> int:
|
||||
return self._line_number
|
||||
|
||||
|
||||
class TexFormatter(PyTeXFormatter, ABC):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -102,13 +109,21 @@ class TexFormatter(PyTeXFormatter, ABC):
|
|||
self._macros = macros
|
||||
|
||||
def _handle_macro(self, macro: Macro):
|
||||
replacement, shipout = macro.apply(
|
||||
self.line_stream.current_line(),
|
||||
self
|
||||
)
|
||||
try:
|
||||
replacement, shipout = macro.apply(
|
||||
self.line_stream.current_line(),
|
||||
self
|
||||
)
|
||||
except PyTeXMacroError as e:
|
||||
e.add_explanation('While applying macro')
|
||||
raise e
|
||||
if shipout:
|
||||
self.line_stream.pop_line()
|
||||
self._shipout_line(replacement)
|
||||
if isinstance(replacement, str):
|
||||
self._shipout_line(replacement)
|
||||
else:
|
||||
for line in replacement:
|
||||
self._shipout_line(line)
|
||||
else:
|
||||
if isinstance(replacement, str):
|
||||
self.line_stream.set_line(replacement)
|
||||
|
@ -150,13 +165,13 @@ class TexFormatter(PyTeXFormatter, ABC):
|
|||
def mode(self, mode: FormatterMode) -> None:
|
||||
self._mode = mode
|
||||
|
||||
def _get_provides_text(self) -> str:
|
||||
def _get_provides_text(self, provided_type: str) -> str:
|
||||
if self.config.has_description:
|
||||
if self.config.tex_flavour == TeXFlavour.LaTeX2e:
|
||||
return \
|
||||
r'\Provides%s{%s}[%s - %s]' \
|
||||
% (
|
||||
self.config.tex_type.value.capitalize(),
|
||||
provided_type,
|
||||
self.name,
|
||||
self.attribute_dict[FormatterProperty.date.value],
|
||||
self.attribute_dict[FormatterProperty.description.value]
|
||||
|
@ -166,7 +181,7 @@ class TexFormatter(PyTeXFormatter, ABC):
|
|||
return \
|
||||
'\\ProvidesExpl%s { %s } { %s } { %s }\n { %s }' \
|
||||
% (
|
||||
self.config.tex_type.value.capitalize(),
|
||||
provided_type,
|
||||
self.name,
|
||||
self.attribute_dict[FormatterProperty.date.value],
|
||||
self.config.version,
|
||||
|
@ -183,14 +198,22 @@ class TexFormatter(PyTeXFormatter, ABC):
|
|||
|
||||
def format_document(self) -> None:
|
||||
while not self.line_stream.exhausted:
|
||||
recent_replacement = True
|
||||
while recent_replacement:
|
||||
recent_replacement = False
|
||||
for macro in self.macros:
|
||||
if macro.matches(self.line_stream.current_line()):
|
||||
self._handle_macro(macro)
|
||||
recent_replacement = True
|
||||
break
|
||||
try:
|
||||
recent_replacement = True
|
||||
while recent_replacement:
|
||||
recent_replacement = False
|
||||
for macro in self.macros:
|
||||
if macro.matches(self.line_stream.current_line()):
|
||||
try:
|
||||
self._handle_macro(macro)
|
||||
except PyTeXMacroError as e:
|
||||
e.add_explanation('while handling macro')
|
||||
raise e
|
||||
recent_replacement = True
|
||||
break
|
||||
except PyTeXMacroError as e:
|
||||
e.add_explanation(f'in line {self.line_stream.line_number} ({self.line_stream.current_line().rstrip()})')
|
||||
pass
|
||||
self._shipout_line(self._post_process_line(
|
||||
self.line_stream.pop_line()
|
||||
))
|
||||
|
|
Loading…
Reference in a new issue