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):
|
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):
|
class PyTeXFormattingError(PyTeXError):
|
||||||
|
|
|
@ -158,7 +158,9 @@ class MacroCodeBeginMacro(SingleLineMacro):
|
||||||
|
|
||||||
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
||||||
if not formatter.mode == FormatterMode.meta:
|
if not formatter.mode == FormatterMode.meta:
|
||||||
raise PyTeXInvalidBeginMacroCodeUsageError
|
raise PyTeXInvalidBeginMacroCodeUsageError(
|
||||||
|
r"\begin{macrocode} used outside meta context"
|
||||||
|
)
|
||||||
formatter.mode = FormatterMode.macrocode
|
formatter.mode = FormatterMode.macrocode
|
||||||
return r'% \begin{macrocode}'
|
return r'% \begin{macrocode}'
|
||||||
|
|
||||||
|
@ -169,7 +171,9 @@ class MacroCodeEndMacro(SingleLineMacro):
|
||||||
|
|
||||||
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
def _apply(self, line: str, formatter) -> Union[str, List[str]]:
|
||||||
if not formatter.mode == FormatterMode.macrocode:
|
if not formatter.mode == FormatterMode.macrocode:
|
||||||
raise PyTeXInvalidEndMacroCodeUsageError
|
raise PyTeXInvalidEndMacroCodeUsageError(
|
||||||
|
r"\end{macrocode} used outside macrocode context"
|
||||||
|
)
|
||||||
formatter.mode = FormatterMode.meta
|
formatter.mode = FormatterMode.meta
|
||||||
return r'% \end{macrocode}'
|
return r'% \end{macrocode}'
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from .macros import Macro
|
||||||
from .pytex_formatter import PyTeXFormatter
|
from .pytex_formatter import PyTeXFormatter
|
||||||
from .enums import *
|
from .enums import *
|
||||||
from ..logger import logger
|
from ..logger import logger
|
||||||
|
from .errors import *
|
||||||
|
|
||||||
|
|
||||||
class LineStream:
|
class LineStream:
|
||||||
|
@ -15,6 +16,7 @@ class LineStream:
|
||||||
self._handle = open(filename, 'r')
|
self._handle = open(filename, 'r')
|
||||||
self._cached_lines: List[str] = []
|
self._cached_lines: List[str] = []
|
||||||
self._file_exhausted: bool = False
|
self._file_exhausted: bool = False
|
||||||
|
self._line_number: int = 0
|
||||||
|
|
||||||
def current_line(self):
|
def current_line(self):
|
||||||
return self.future_line(0)
|
return self.future_line(0)
|
||||||
|
@ -47,6 +49,7 @@ class LineStream:
|
||||||
self._cached_lines.append(
|
self._cached_lines.append(
|
||||||
self._handle.readline()
|
self._handle.readline()
|
||||||
)
|
)
|
||||||
|
self._line_number += 1
|
||||||
if self._cached_lines[-1] == '':
|
if self._cached_lines[-1] == '':
|
||||||
self._handle.close()
|
self._handle.close()
|
||||||
self._file_exhausted = True
|
self._file_exhausted = True
|
||||||
|
@ -57,6 +60,10 @@ class LineStream:
|
||||||
b = len(self._cached_lines) == 0
|
b = len(self._cached_lines) == 0
|
||||||
return a & b
|
return a & b
|
||||||
|
|
||||||
|
@property
|
||||||
|
def line_number(self) -> int:
|
||||||
|
return self._line_number
|
||||||
|
|
||||||
|
|
||||||
class TexFormatter(PyTeXFormatter, ABC):
|
class TexFormatter(PyTeXFormatter, ABC):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -102,13 +109,21 @@ class TexFormatter(PyTeXFormatter, ABC):
|
||||||
self._macros = macros
|
self._macros = macros
|
||||||
|
|
||||||
def _handle_macro(self, macro: Macro):
|
def _handle_macro(self, macro: Macro):
|
||||||
|
try:
|
||||||
replacement, shipout = macro.apply(
|
replacement, shipout = macro.apply(
|
||||||
self.line_stream.current_line(),
|
self.line_stream.current_line(),
|
||||||
self
|
self
|
||||||
)
|
)
|
||||||
|
except PyTeXMacroError as e:
|
||||||
|
e.add_explanation('While applying macro')
|
||||||
|
raise e
|
||||||
if shipout:
|
if shipout:
|
||||||
self.line_stream.pop_line()
|
self.line_stream.pop_line()
|
||||||
|
if isinstance(replacement, str):
|
||||||
self._shipout_line(replacement)
|
self._shipout_line(replacement)
|
||||||
|
else:
|
||||||
|
for line in replacement:
|
||||||
|
self._shipout_line(line)
|
||||||
else:
|
else:
|
||||||
if isinstance(replacement, str):
|
if isinstance(replacement, str):
|
||||||
self.line_stream.set_line(replacement)
|
self.line_stream.set_line(replacement)
|
||||||
|
@ -150,13 +165,13 @@ class TexFormatter(PyTeXFormatter, ABC):
|
||||||
def mode(self, mode: FormatterMode) -> None:
|
def mode(self, mode: FormatterMode) -> None:
|
||||||
self._mode = mode
|
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.has_description:
|
||||||
if self.config.tex_flavour == TeXFlavour.LaTeX2e:
|
if self.config.tex_flavour == TeXFlavour.LaTeX2e:
|
||||||
return \
|
return \
|
||||||
r'\Provides%s{%s}[%s - %s]' \
|
r'\Provides%s{%s}[%s - %s]' \
|
||||||
% (
|
% (
|
||||||
self.config.tex_type.value.capitalize(),
|
provided_type,
|
||||||
self.name,
|
self.name,
|
||||||
self.attribute_dict[FormatterProperty.date.value],
|
self.attribute_dict[FormatterProperty.date.value],
|
||||||
self.attribute_dict[FormatterProperty.description.value]
|
self.attribute_dict[FormatterProperty.description.value]
|
||||||
|
@ -166,7 +181,7 @@ class TexFormatter(PyTeXFormatter, ABC):
|
||||||
return \
|
return \
|
||||||
'\\ProvidesExpl%s { %s } { %s } { %s }\n { %s }' \
|
'\\ProvidesExpl%s { %s } { %s } { %s }\n { %s }' \
|
||||||
% (
|
% (
|
||||||
self.config.tex_type.value.capitalize(),
|
provided_type,
|
||||||
self.name,
|
self.name,
|
||||||
self.attribute_dict[FormatterProperty.date.value],
|
self.attribute_dict[FormatterProperty.date.value],
|
||||||
self.config.version,
|
self.config.version,
|
||||||
|
@ -183,14 +198,22 @@ class TexFormatter(PyTeXFormatter, ABC):
|
||||||
|
|
||||||
def format_document(self) -> None:
|
def format_document(self) -> None:
|
||||||
while not self.line_stream.exhausted:
|
while not self.line_stream.exhausted:
|
||||||
|
try:
|
||||||
recent_replacement = True
|
recent_replacement = True
|
||||||
while recent_replacement:
|
while recent_replacement:
|
||||||
recent_replacement = False
|
recent_replacement = False
|
||||||
for macro in self.macros:
|
for macro in self.macros:
|
||||||
if macro.matches(self.line_stream.current_line()):
|
if macro.matches(self.line_stream.current_line()):
|
||||||
|
try:
|
||||||
self._handle_macro(macro)
|
self._handle_macro(macro)
|
||||||
|
except PyTeXMacroError as e:
|
||||||
|
e.add_explanation('while handling macro')
|
||||||
|
raise e
|
||||||
recent_replacement = True
|
recent_replacement = True
|
||||||
break
|
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._shipout_line(self._post_process_line(
|
||||||
self.line_stream.pop_line()
|
self.line_stream.pop_line()
|
||||||
))
|
))
|
||||||
|
|
Loading…
Reference in a new issue