From abf60f505293a38372158b71c94d2173e30afa8a Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Wed, 6 Dec 2023 23:13:43 -0800 Subject: [PATCH] Future resolvable --- .../script/functions/array_functions.py | 17 +++++++---------- src/ytdl_sub/script/functions/map_functions.py | 7 +++---- .../script/functions/regex_functions.py | 9 +++------ src/ytdl_sub/script/types/array.py | 13 +++++++++---- src/ytdl_sub/script/types/function.py | 4 ++-- src/ytdl_sub/script/types/resolvable.py | 7 +++++++ src/ytdl_sub/script/utils/type_checking.py | 4 +++- tests/unit/script/types/test_array.py | 13 ++++++------- tests/unit/script/types/test_lambda_function.py | 6 +++--- tests/unit/script/types/test_map.py | 1 - 10 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/ytdl_sub/script/functions/array_functions.py b/src/ytdl_sub/script/functions/array_functions.py index b2e31fee..1e8c3c38 100644 --- a/src/ytdl_sub/script/functions/array_functions.py +++ b/src/ytdl_sub/script/functions/array_functions.py @@ -2,7 +2,6 @@ from typing import List from typing import Optional from ytdl_sub.script.types.array import Array -from ytdl_sub.script.types.array import ResolvedArray from ytdl_sub.script.types.resolvable import AnyArgument from ytdl_sub.script.types.resolvable import Boolean from ytdl_sub.script.types.resolvable import Integer @@ -23,7 +22,7 @@ class ArrayFunctions: for array in arrays: output.extend(array.value) - return ResolvedArray(output) + return Array(output) @staticmethod def array_at(array: Array, idx: Integer) -> Resolvable: @@ -61,8 +60,8 @@ class ArrayFunctions: Returns the slice of the Array. """ if end is not None: - return ResolvedArray(array.value[start.value : end.value]) - return ResolvedArray(array.value[start.value :]) + return Array(array.value[start.value : end.value]) + return Array(array.value[start.value :]) @staticmethod def array_flatten(array: Array) -> Array: @@ -76,14 +75,14 @@ class ArrayFunctions: else: output.append(elem) - return ResolvedArray(output) + return Array(output) @staticmethod def array_reverse(array: Array) -> Array: """ Reverse an Array. """ - return ResolvedArray(list(reversed(array.value))) + return Array(list(reversed(array.value))) # pylint: disable=unused-argument @@ -92,7 +91,7 @@ class ArrayFunctions: """ Apply a lambda function on every element in the Array. """ - return ResolvedArray([ResolvedArray([val]) for val in array.value]) + return Array([Array([val]) for val in array.value]) @staticmethod def array_enumerate(array: Array, lambda_function: LambdaTwo) -> Array: @@ -100,8 +99,6 @@ class ArrayFunctions: Apply a lambda function on every element in the Array, where each arg passed to the lambda function is ``idx, element`` as two separate args. """ - return ResolvedArray( - [ResolvedArray([Integer(idx), val]) for idx, val in enumerate(array.value)] - ) + return Array([Array([Integer(idx), val]) for idx, val in enumerate(array.value)]) # pylint: enable=unused-argument diff --git a/src/ytdl_sub/script/functions/map_functions.py b/src/ytdl_sub/script/functions/map_functions.py index 8194d756..51f8ebee 100644 --- a/src/ytdl_sub/script/functions/map_functions.py +++ b/src/ytdl_sub/script/functions/map_functions.py @@ -1,7 +1,6 @@ from typing import Optional from ytdl_sub.script.types.array import Array -from ytdl_sub.script.types.array import ResolvedArray from ytdl_sub.script.types.map import Map from ytdl_sub.script.types.resolvable import AnyArgument from ytdl_sub.script.types.resolvable import Boolean @@ -43,7 +42,7 @@ class MapFunctions: Apply a lambda function on the Map, where each arg passed to the lambda function is ``key, value`` as two separate args. """ - return ResolvedArray([ResolvedArray([key, value]) for key, value in mapping.value.items()]) + return Array([Array([key, value]) for key, value in mapping.value.items()]) @staticmethod def map_enumerate(mapping: Map, lambda_function: LambdaThree) -> Array: @@ -51,9 +50,9 @@ class MapFunctions: Apply a lambda function on the Map, where each arg passed to the lambda function is ``idx, key, value`` as three separate args. """ - return ResolvedArray( + return Array( [ - ResolvedArray([Integer(idx), key_value[0], key_value[1]]) + Array([Integer(idx), key_value[0], key_value[1]]) for idx, key_value in enumerate(mapping.value.items()) ] ) diff --git a/src/ytdl_sub/script/functions/regex_functions.py b/src/ytdl_sub/script/functions/regex_functions.py index 7408221c..2abca1ec 100644 --- a/src/ytdl_sub/script/functions/regex_functions.py +++ b/src/ytdl_sub/script/functions/regex_functions.py @@ -3,17 +3,14 @@ from typing import AnyStr from typing import Match from ytdl_sub.script.types.array import Array -from ytdl_sub.script.types.array import ResolvedArray from ytdl_sub.script.types.resolvable import String -def _re_output_to_array(re_out: Match[AnyStr] | None) -> ResolvedArray: +def _re_output_to_array(re_out: Match[AnyStr] | None) -> Array: if re_out is None: - return ResolvedArray([]) + return Array([]) - return ResolvedArray( - list([String(re_out.string)]) + list(String(group) for group in re_out.groups()) - ) + return Array(list([String(re_out.string)]) + list(String(group) for group in re_out.groups())) class RegexFunctions: diff --git a/src/ytdl_sub/script/types/array.py b/src/ytdl_sub/script/types/array.py index e6633f86..a0790d3f 100644 --- a/src/ytdl_sub/script/types/array.py +++ b/src/ytdl_sub/script/types/array.py @@ -2,9 +2,11 @@ from dataclasses import dataclass from typing import Any from typing import Dict from typing import List +from typing import Type from ytdl_sub.script.types.resolvable import AnyArgument from ytdl_sub.script.types.resolvable import Argument +from ytdl_sub.script.types.resolvable import FutureResolvable from ytdl_sub.script.types.resolvable import NonHashable from ytdl_sub.script.types.resolvable import Resolvable from ytdl_sub.script.types.resolvable import ResolvableToJson @@ -13,7 +15,7 @@ from ytdl_sub.script.types.variable_dependency import VariableDependency @dataclass(frozen=True) -class Array(NonHashable): +class _Array(NonHashable): value: List[Resolvable] @classmethod @@ -22,7 +24,7 @@ class Array(NonHashable): @dataclass(frozen=True) -class UnresolvedArray(Array, VariableDependency, AnyArgument): +class UnresolvedArray(_Array, VariableDependency, FutureResolvable): value: List[Argument] @property @@ -34,7 +36,7 @@ class UnresolvedArray(Array, VariableDependency, AnyArgument): resolved_variables: Dict[Variable, Resolvable], custom_functions: Dict[str, "VariableDependency"], ) -> Resolvable: - return ResolvedArray( + return Array( [ self._resolve_argument_type( arg=arg, @@ -45,9 +47,12 @@ class UnresolvedArray(Array, VariableDependency, AnyArgument): ] ) + def future_resolvable_type(self) -> Type[Resolvable]: + return Array + @dataclass(frozen=True) -class ResolvedArray(Array, ResolvableToJson): +class Array(_Array, ResolvableToJson): @property def native(self) -> Any: return [val.native for val in self.value] diff --git a/src/ytdl_sub/script/types/function.py b/src/ytdl_sub/script/types/function.py index 59aaf8e3..40fa95c8 100644 --- a/src/ytdl_sub/script/types/function.py +++ b/src/ytdl_sub/script/types/function.py @@ -9,7 +9,7 @@ from typing import Type from typing import Union from ytdl_sub.script.functions import Functions -from ytdl_sub.script.types.array import ResolvedArray +from ytdl_sub.script.types.array import Array from ytdl_sub.script.types.array import UnresolvedArray from ytdl_sub.script.types.resolvable import Argument from ytdl_sub.script.types.resolvable import BuiltInFunctionType @@ -151,7 +151,7 @@ class BuiltInFunction(Function, BuiltInFunctionType): f"Runtime error occurred when executing the function %{self.name}: {str(exc)}" ) from exc - assert isinstance(lambda_args, ResolvedArray) + assert isinstance(lambda_args, Array) return self._resolve_argument_type( arg=UnresolvedArray( diff --git a/src/ytdl_sub/script/types/resolvable.py b/src/ytdl_sub/script/types/resolvable.py index cc96885e..17f68908 100644 --- a/src/ytdl_sub/script/types/resolvable.py +++ b/src/ytdl_sub/script/types/resolvable.py @@ -87,6 +87,13 @@ class Resolvable(AnyArgument, ABC): return self.value +@dataclass(frozen=True) +class FutureResolvable(AnyArgument, ABC): + @abstractmethod + def future_resolvable_type(self) -> Type[Resolvable]: + pass + + @dataclass(frozen=True) class Hashable(Resolvable, ABC): pass diff --git a/src/ytdl_sub/script/utils/type_checking.py b/src/ytdl_sub/script/utils/type_checking.py index d4fe36e2..49b65f60 100644 --- a/src/ytdl_sub/script/utils/type_checking.py +++ b/src/ytdl_sub/script/utils/type_checking.py @@ -12,6 +12,7 @@ from typing import get_origin from ytdl_sub.script.types.resolvable import Argument from ytdl_sub.script.types.resolvable import BuiltInFunctionType from ytdl_sub.script.types.resolvable import FunctionType +from ytdl_sub.script.types.resolvable import FutureResolvable from ytdl_sub.script.types.resolvable import Lambda from ytdl_sub.script.types.resolvable import LambdaThree from ytdl_sub.script.types.resolvable import LambdaTwo @@ -65,11 +66,12 @@ def is_type_compatible( arg_type: Type[NamedType] = arg.__class__ if isinstance(arg, BuiltInFunctionType): arg_type = arg.output_type() # built-in function + elif isinstance(arg, FutureResolvable): + arg_type = arg.future_resolvable_type() elif isinstance(arg, FunctionType): return True # custom-function, can be anything, so pass for now elif isinstance(arg, Variable): return True # unresolved variables can be anything, so pass for now - if is_union(expected_arg_type): # See if the arg is a valid against the union valid_type = False diff --git a/tests/unit/script/types/test_array.py b/tests/unit/script/types/test_array.py index 554e9fa4..3b8fe1e2 100644 --- a/tests/unit/script/types/test_array.py +++ b/tests/unit/script/types/test_array.py @@ -6,8 +6,7 @@ from ytdl_sub.script.parser import _UNEXPECTED_CHAR_ARGUMENT from ytdl_sub.script.parser import _UNEXPECTED_COMMA_ARGUMENT from ytdl_sub.script.parser import ParsedArgType from ytdl_sub.script.script import Script -from ytdl_sub.script.types.array import ResolvedArray -from ytdl_sub.script.types.resolvable import Boolean +from ytdl_sub.script.types.array import Array from ytdl_sub.script.types.resolvable import Float from ytdl_sub.script.types.resolvable import String from ytdl_sub.script.utils.exceptions import InvalidSyntaxException @@ -16,7 +15,7 @@ from ytdl_sub.script.utils.exceptions import InvalidSyntaxException class TestArray: def test_return(self): assert Script({"array": "{['a', 3.14]}"}).resolve() == { - "array": ResolvedArray([String("a"), Float(3.14)]) + "array": Array([String("a"), Float(3.14)]) } def test_return_as_str(self): @@ -28,13 +27,13 @@ class TestArray: assert Script( {"array": "{['level1', ['level2', ['level3', 'level3'], 'level2'], 'level1']}"} ).resolve() == { - "array": ResolvedArray( + "array": Array( [ String("level1"), - ResolvedArray( + Array( [ String("level2"), - ResolvedArray([String("level3"), String("level3")]), + Array([String("level3"), String("level3")]), String("level2"), ], ), @@ -53,7 +52,7 @@ class TestArray: ], ) def test_empty(self, array: str): - assert Script({"array": array}).resolve() == {"array": ResolvedArray([])} + assert Script({"array": array}).resolve() == {"array": Array([])} @pytest.mark.parametrize( "array", diff --git a/tests/unit/script/types/test_lambda_function.py b/tests/unit/script/types/test_lambda_function.py index 631287a9..9eae411a 100644 --- a/tests/unit/script/types/test_lambda_function.py +++ b/tests/unit/script/types/test_lambda_function.py @@ -3,7 +3,7 @@ import re import pytest from ytdl_sub.script.script import Script -from ytdl_sub.script.types.array import ResolvedArray +from ytdl_sub.script.types.array import Array from ytdl_sub.script.types.resolvable import Integer from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments @@ -12,7 +12,7 @@ class TestLambdaFunction: def test_lambda_with_custom_function(self): assert Script( {"%times_two": "{%mul($0, 2)}", "wip": "{%array_apply([1, 2, 3], %times_two)}"} - ).resolve() == {"wip": ResolvedArray([Integer(2), Integer(4), Integer(6)])} + ).resolve() == {"wip": Array([Integer(2), Integer(4), Integer(6)])} def test_conditional_lambda_with_custom_functions(self): assert Script( @@ -21,7 +21,7 @@ class TestLambdaFunction: "%times_two": "{%mul($0, 2)}", "wip": "{%array_apply([1, 2, 3], %if(False, %times_two, %times_three))}", } - ).resolve() == {"wip": ResolvedArray([Integer(3), Integer(6), Integer(9)])} + ).resolve() == {"wip": Array([Integer(3), Integer(6), Integer(9)])} def test_nested_custom_functions(self): assert Script( diff --git a/tests/unit/script/types/test_map.py b/tests/unit/script/types/test_map.py index 36603a77..9dfd39af 100644 --- a/tests/unit/script/types/test_map.py +++ b/tests/unit/script/types/test_map.py @@ -11,7 +11,6 @@ from ytdl_sub.script.parser import MAP_MISSING_KEY from ytdl_sub.script.parser import ParsedArgType from ytdl_sub.script.script import Script from ytdl_sub.script.types.map import ResolvedMap -from ytdl_sub.script.types.resolvable import Boolean from ytdl_sub.script.types.resolvable import Float from ytdl_sub.script.types.resolvable import String from ytdl_sub.script.utils.exceptions import InvalidSyntaxException