From b5622ecc54c150e045f15a05d8212a77b503035b Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Thu, 9 Nov 2023 19:28:33 -0800 Subject: [PATCH] better exception formatting --- src/ytdl_sub/script/parser.py | 80 ++++++++++++++++++----------- tests/unit/script/types/test_map.py | 4 +- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/ytdl_sub/script/parser.py b/src/ytdl_sub/script/parser.py index 8aea41ff..9855edbc 100644 --- a/src/ytdl_sub/script/parser.py +++ b/src/ytdl_sub/script/parser.py @@ -1,3 +1,4 @@ +from contextlib import contextmanager from typing import Dict from typing import List from typing import Optional @@ -37,6 +38,24 @@ class _Parser: """ return self._syntax_tree + @contextmanager + def _error_formatting(self) -> None: + parked_pos = self._pos + try: + yield + except InvalidSyntaxException as exc: + border = 4 + text_left = max(0, parked_pos - border) + text_right = min(len(self._text), self._pos + border) + text_len = text_right - text_left + + raise InvalidSyntaxException( + "Invalid syntax:\n" + f" {self._text[text_left:text_right]}\n" + f" {' ' * border}{'^' * text_len}\n\n" + f"{str(exc)}" + ) from exc + def _read(self, increment_pos: bool = True, length: int = 1) -> Optional[str]: try: ch = self._text[self._pos : (self._pos + length)] @@ -227,38 +246,39 @@ class _Parser: key: Optional[ArgumentType] = None in_comma = False - while ch := self._read(increment_pos=False): - if ch == "}": - if key is not None: - raise InvalidSyntaxException("Map has a key with no value") + with self._error_formatting(): + while ch := self._read(increment_pos=False): + if ch == "}": + if key is not None: + raise InvalidSyntaxException("Map has a key with no value") - self._pos += 1 - return UnresolvedMap(value=output) - elif ch == ",": - if in_comma: - raise StringFormattingException("Comma followed by comma") - if key is not None: - raise InvalidSyntaxException("Map has a key with no value") - if output is None: - raise StringFormattingException("Empty dict with comma") - in_comma = True - self._pos += 1 - elif key is None: - in_comma = False - key_args = self._parse_args(breaking_chars=":") - if len(key_args) != 1: - raise StringFormattingException("Lazy parsing but got mlutiple args") - key = key_args[0] - elif key is not None and ch == ":": - self._pos += 1 - value_args = self._parse_args(breaking_chars=",}") - if len(value_args) != 1: - raise InvalidSyntaxException("Map has a key with no value") + self._pos += 1 + return UnresolvedMap(value=output) + elif ch == ",": + if in_comma: + raise StringFormattingException("Comma followed by comma") + if key is not None: + raise InvalidSyntaxException("Map has a key with no value") + if output is None: + raise StringFormattingException("Empty dict with comma") + in_comma = True + self._pos += 1 + elif key is None: + in_comma = False + key_args = self._parse_args(breaking_chars=":") + if len(key_args) != 1: + raise StringFormattingException("Lazy parsing but got mlutiple args") + key = key_args[0] + elif key is not None and ch == ":": + self._pos += 1 + value_args = self._parse_args(breaking_chars=",}") + if len(value_args) != 1: + raise InvalidSyntaxException("Map has a key with no value") - output[key] = value_args[0] - key = None - else: - raise StringFormattingException("Invalid map") + output[key] = value_args[0] + key = None + else: + raise StringFormattingException("Invalid map") def _parse(self) -> SyntaxTree: bracket_counter = 0 diff --git a/tests/unit/script/types/test_map.py b/tests/unit/script/types/test_map.py index 73a4e28a..75543071 100644 --- a/tests/unit/script/types/test_map.py +++ b/tests/unit/script/types/test_map.py @@ -72,5 +72,5 @@ class TestMap: ], ) def test_key_has_no_value(self, value: str): - with pytest.raises(InvalidSyntaxException, match=re.escape("Map has a key with no value")): - Script({"map": value}).resolve() + # with pytest.raises(InvalidSyntaxException, match=re.escape("Map has a key with no value")): + Script({"map": value}).resolve()