better error handling for incompatible args
This commit is contained in:
parent
8fe4460ea5
commit
96182af2c1
5 changed files with 32 additions and 12 deletions
|
|
@ -19,6 +19,7 @@ from ytdl_sub.script.types.variable import Variable
|
||||||
from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments
|
from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments
|
||||||
from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
|
from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
|
||||||
from ytdl_sub.script.utils.exceptions import UnreachableSyntaxException
|
from ytdl_sub.script.utils.exceptions import UnreachableSyntaxException
|
||||||
|
from ytdl_sub.script.utils.exceptions import UserException
|
||||||
from ytdl_sub.script.utils.parser_exception_formatter import ParserExceptionFormatter
|
from ytdl_sub.script.utils.parser_exception_formatter import ParserExceptionFormatter
|
||||||
from ytdl_sub.utils.exceptions import StringFormattingException
|
from ytdl_sub.utils.exceptions import StringFormattingException
|
||||||
from ytdl_sub.validators.string_formatter_validators import is_valid_source_variable_name
|
from ytdl_sub.validators.string_formatter_validators import is_valid_source_variable_name
|
||||||
|
|
@ -112,7 +113,7 @@ class _Parser:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._syntax_tree = self._parse()
|
self._syntax_tree = self._parse()
|
||||||
except InvalidSyntaxException as exc:
|
except UserException as exc:
|
||||||
raise ParserExceptionFormatter(
|
raise ParserExceptionFormatter(
|
||||||
self._text, self._error_highlight_pos, self._pos, exc
|
self._text, self._error_highlight_pos, self._pos, exc
|
||||||
).highlight() from exc
|
).highlight() from exc
|
||||||
|
|
@ -329,9 +330,9 @@ class _Parser:
|
||||||
if ch == ")":
|
if ch == ")":
|
||||||
try:
|
try:
|
||||||
return Function.from_name_and_args(name=function_name, args=function_args)
|
return Function.from_name_and_args(name=function_name, args=function_args)
|
||||||
except IncompatibleFunctionArguments as exc:
|
except IncompatibleFunctionArguments:
|
||||||
self._set_highlight_position(function_start_pos)
|
self._set_highlight_position(function_start_pos)
|
||||||
raise InvalidSyntaxException(exc) from exc
|
raise
|
||||||
|
|
||||||
if ch != "(":
|
if ch != "(":
|
||||||
function_name += ch
|
function_name += ch
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ class BuiltInFunction(Function):
|
||||||
|
|
||||||
received_args_str = f"({', '.join([name for name in received_type_names])})"
|
received_args_str = f"({', '.join([name for name in received_type_names])})"
|
||||||
|
|
||||||
return f"Expected {self.input_spec.expected_args_str()}.\nReceived {received_args_str}"
|
return f"Expected {self.input_spec.expected_args_str()}\nReceived {received_args_str}"
|
||||||
|
|
||||||
def validate_args(self) -> "BuiltInFunction":
|
def validate_args(self) -> "BuiltInFunction":
|
||||||
if not self.input_spec.is_compatible(input_args=self.args):
|
if not self.input_spec.is_compatible(input_args=self.args):
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
|
from abc import ABC
|
||||||
|
|
||||||
from ytdl_sub.utils.exceptions import ValidationException
|
from ytdl_sub.utils.exceptions import ValidationException
|
||||||
|
|
||||||
|
|
||||||
class InvalidSyntaxException(ValidationException):
|
class UserException(ValidationException, ABC):
|
||||||
|
"""It's the user's fault!"""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidSyntaxException(UserException):
|
||||||
"""Syntax is incorrect"""
|
"""Syntax is incorrect"""
|
||||||
|
|
||||||
|
|
||||||
class IncompatibleFunctionArguments(ValidationException):
|
class IncompatibleFunctionArguments(UserException):
|
||||||
"""Function has invalid arguments"""
|
"""Function has invalid arguments"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
|
from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
|
||||||
|
from ytdl_sub.script.utils.exceptions import UserException
|
||||||
|
|
||||||
|
TUserException = TypeVar("TUserException", bound=UserException)
|
||||||
|
|
||||||
|
|
||||||
class ParserExceptionFormatter:
|
class ParserExceptionFormatter:
|
||||||
def __init__(self, text: str, start: int, end: int, exception: InvalidSyntaxException):
|
def __init__(self, text: str, start: int, end: int, exception: TUserException):
|
||||||
self._text = text
|
self._text = text
|
||||||
self._start = start
|
self._start = start
|
||||||
self._end = end
|
self._end = end
|
||||||
|
|
@ -65,10 +69,10 @@ class ParserExceptionFormatter:
|
||||||
|
|
||||||
return "\n" + "\n".join(to_return)
|
return "\n" + "\n".join(to_return)
|
||||||
|
|
||||||
def highlight(self) -> InvalidSyntaxException:
|
def highlight(self) -> TUserException:
|
||||||
if self.is_multi_line:
|
if self.is_multi_line:
|
||||||
invalid_syntax = self.exception_text_lines(border_lines=3)
|
invalid_syntax = self.exception_text_lines(border_lines=3)
|
||||||
else:
|
else:
|
||||||
invalid_syntax = self.exception_text(border=20)
|
invalid_syntax = self.exception_text(border=20)
|
||||||
|
|
||||||
return InvalidSyntaxException(f"{invalid_syntax}\n{str(self._exception)}")
|
return self._exception.__class__(f"{invalid_syntax}\n{str(self._exception)}")
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,14 @@ from ytdl_sub.script.types.resolvable import Boolean
|
||||||
from ytdl_sub.script.types.resolvable import Float
|
from ytdl_sub.script.types.resolvable import Float
|
||||||
from ytdl_sub.script.types.resolvable import Integer
|
from ytdl_sub.script.types.resolvable import Integer
|
||||||
from ytdl_sub.script.types.resolvable import String
|
from ytdl_sub.script.types.resolvable import String
|
||||||
|
from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments
|
||||||
from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
|
from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
|
||||||
|
|
||||||
|
|
||||||
|
def _incompatible_arguments_match(expected: str, recieved: str) -> str:
|
||||||
|
return re.escape(f"Expected ({expected})\nReceived ({recieved})")
|
||||||
|
|
||||||
|
|
||||||
class TestFunction:
|
class TestFunction:
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"function_str, expected_output",
|
"function_str, expected_output",
|
||||||
|
|
@ -66,9 +71,13 @@ class TestFunction:
|
||||||
"key"
|
"key"
|
||||||
)
|
)
|
||||||
}"""
|
}"""
|
||||||
assert Script({"func": function_str}).resolve() == {
|
with pytest.raises(
|
||||||
"func": String("winner"),
|
IncompatibleFunctionArguments,
|
||||||
}
|
match=_incompatible_arguments_match(
|
||||||
|
expected="Map, Hashable, Optional", recieved="%if(...)->Union[Map, Array], String"
|
||||||
|
),
|
||||||
|
):
|
||||||
|
Script({"func": function_str}).resolve()
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"function_str", ["{%array_at({'a': 'dict?'}, 1)}" "{%array_extend('not', 'array')}"]
|
"function_str", ["{%array_at({'a': 'dict?'}, 1)}" "{%array_extend('not', 'array')}"]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue