single cycle custom func

This commit is contained in:
Jesse Bannon 2023-11-21 18:01:54 -08:00
parent 4c70e3fe0b
commit 2aa24ba67e
3 changed files with 21 additions and 1 deletions

View file

@ -18,6 +18,7 @@ from ytdl_sub.script.types.variable import FunctionArgument
from ytdl_sub.script.types.variable import Variable from ytdl_sub.script.types.variable import Variable
from ytdl_sub.script.utils.exception_formatters import ParserExceptionFormatter from ytdl_sub.script.utils.exception_formatters import ParserExceptionFormatter
from ytdl_sub.script.utils.exceptions import UNREACHABLE from ytdl_sub.script.utils.exceptions import UNREACHABLE
from ytdl_sub.script.utils.exceptions import CycleDetected
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 UserException from ytdl_sub.script.utils.exceptions import UserException
@ -334,8 +335,14 @@ class _Parser:
while ch := self._read(): while ch := self._read():
if ch == ")": if ch == ")":
if function_args is not None:
# Had '(' to indicate there are args # Had '(' to indicate there are args
if function_args is not None:
if self._custom_function_name == function_name:
self._set_highlight_position(function_start_pos)
raise CycleDetected(
f"The custom function %{function_name} cannot call itself."
)
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: except IncompatibleFunctionArguments:

View file

@ -19,6 +19,10 @@ class FunctionDoesNotExist(UserException):
"""Tried to use a function that does not exist""" """Tried to use a function that does not exist"""
class CycleDetected(UserException):
"""A cycle exists within a user's script"""
class FunctionRuntimeException(ValueError): class FunctionRuntimeException(ValueError):
"""Exception thrown when a ytdl-sub function has an error occur at runtime""" """Exception thrown when a ytdl-sub function has an error occur at runtime"""

View file

@ -8,6 +8,7 @@ from ytdl_sub.script.types.array import ResolvedArray
from ytdl_sub.script.types.resolvable import Boolean from ytdl_sub.script.types.resolvable import Boolean
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 CycleDetected
from ytdl_sub.script.utils.exceptions import FunctionDoesNotExist from ytdl_sub.script.utils.exceptions import FunctionDoesNotExist
from ytdl_sub.script.utils.exceptions import FunctionRuntimeException from ytdl_sub.script.utils.exceptions import FunctionRuntimeException
from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments
@ -117,6 +118,14 @@ class TestFunction:
): ):
Script({"dne": "{%throw}"}).resolve() Script({"dne": "{%throw}"}).resolve()
def test_custom_function_cycle(self):
with pytest.raises(
CycleDetected, match=re.escape("The custom function %cycle_func cannot call itself.")
):
Script(
{"%cycle_func": "{%mul(%cycle_func(1), $0)}", "output": "{%cycle_func(1)}"}
).resolve()
def test_lambda_with_custom_function(self): def test_lambda_with_custom_function(self):
assert Script( assert Script(
{"%times_two": "{%mul($0, 2)}", "wip": "{%array_apply([1, 2, 3], %times_two)}"} {"%times_two": "{%mul($0, 2)}", "wip": "{%array_apply([1, 2, 3], %times_two)}"}