diff --git a/src/ytdl_sub/script/script.py b/src/ytdl_sub/script/script.py index acaa5d8a..56cf39e5 100644 --- a/src/ytdl_sub/script/script.py +++ b/src/ytdl_sub/script/script.py @@ -15,6 +15,7 @@ from ytdl_sub.script.types.syntax_tree import ResolvedSyntaxTree from ytdl_sub.script.types.syntax_tree import SyntaxTree from ytdl_sub.script.types.variable import FunctionArgument from ytdl_sub.script.types.variable import Variable +from ytdl_sub.script.types.variable_dependency import VariableDependency from ytdl_sub.script.utils.exceptions import UNREACHABLE from ytdl_sub.script.utils.exceptions import CycleDetected from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments @@ -697,9 +698,16 @@ class Script: """ return set(to_function_definition_name(name) for name in self._functions.keys()) - def _to_syntax_tree(self, maybe_resolved: SyntaxTree | Resolvable) -> SyntaxTree: + def _to_syntax_tree( + self, maybe_resolved: SyntaxTree | Resolvable | VariableDependency + ) -> SyntaxTree: if isinstance(maybe_resolved, Resolvable): return ResolvedSyntaxTree(ast=[maybe_resolved]) + if isinstance(maybe_resolved, SyntaxTree): + return maybe_resolved + if isinstance(maybe_resolved, VariableDependency): + return SyntaxTree(ast=[maybe_resolved]) + return maybe_resolved def resolve_partial( @@ -718,22 +726,45 @@ class Script: If specifying a filter of variable to resolve, and one of them does not. """ resolved: Dict[Variable, Resolvable] = {} - unresolved: Dict[Variable, SyntaxTree] = { - Variable(name): ast for name, ast in self._variables.items() if name not in unresolvable - } + unresolved: Dict[Variable, VariableDependency] = {} unresolvable: Set[Variable] = {Variable(name) for name in (unresolvable or {})} + for variable_name, definition in self._variables.items(): + assert len(definition.ast) == 1 + arg = definition.ast[0] + variable = Variable(variable_name) + + if variable in unresolvable: + continue + + if isinstance(arg, Resolvable): + resolved[variable] = arg + elif isinstance(arg, Variable): + while isinstance(arg, Variable): + if arg in unresolvable: + break + arg = self._variables[arg.name] + + if resolved_arg := arg.maybe_resolvable: + resolved[variable] = resolved_arg + else: + assert isinstance(arg, SyntaxTree) + unresolved[variable] = arg.ast[0] + else: + unresolved[variable] = arg + partially_resolved = True while partially_resolved: partially_resolved = False for variable in list(unresolved.keys()): - definition = unresolved[variable] maybe_resolved = definition.partial_resolve( - resolved_variables=resolved, custom_functions=self._functions + resolved_variables=resolved, + unresolved_variables=unresolved, + custom_functions=self._functions, ) if isinstance(maybe_resolved, Resolvable): resolved[variable] = maybe_resolved diff --git a/src/ytdl_sub/script/types/array.py b/src/ytdl_sub/script/types/array.py index 56601e0b..eb1ad383 100644 --- a/src/ytdl_sub/script/types/array.py +++ b/src/ytdl_sub/script/types/array.py @@ -51,11 +51,13 @@ class UnresolvedArray(_Array, VariableDependency, FutureResolvable): def partial_resolve( self: TypeT, resolved_variables: Dict[Variable, Resolvable], + unresolved_variables: Dict[Variable, Argument], custom_functions: Dict[str, "VariableDependency"], ) -> TypeT | Resolvable: maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve( args=self.value, resolved_variables=resolved_variables, + unresolved_variables=unresolved_variables, custom_functions=custom_functions, ) diff --git a/src/ytdl_sub/script/types/function.py b/src/ytdl_sub/script/types/function.py index 1b6e08d6..54d89f22 100644 --- a/src/ytdl_sub/script/types/function.py +++ b/src/ytdl_sub/script/types/function.py @@ -88,11 +88,13 @@ class CustomFunction(Function, NamedCustomFunction): def partial_resolve( self, resolved_variables: Dict[Variable, Resolvable], + unresolved_variables: Dict[Variable, Argument], custom_functions: Dict[str, "VariableDependency"], ) -> TypeT | Resolvable: maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve( args=self.args, resolved_variables=resolved_variables, + unresolved_variables=unresolved_variables, custom_functions=custom_functions, ) @@ -338,6 +340,7 @@ class BuiltInFunction(Function, BuiltInFunctionType): def partial_resolve( self, resolved_variables: Dict[Variable, Resolvable], + unresolved_variables: Dict[Variable, Argument], custom_functions: Dict[str, "VariableDependency"], ) -> TypeT | Resolvable: conditional_return_args = self.function_spec.conditional_arg_indices( @@ -358,6 +361,7 @@ class BuiltInFunction(Function, BuiltInFunctionType): maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve( args=self.args, resolved_variables=resolved_variables, + unresolved_variables=unresolved_variables, custom_functions=custom_functions, ) diff --git a/src/ytdl_sub/script/types/map.py b/src/ytdl_sub/script/types/map.py index 841d491c..6f5ef69e 100644 --- a/src/ytdl_sub/script/types/map.py +++ b/src/ytdl_sub/script/types/map.py @@ -59,17 +59,20 @@ class UnresolvedMap(_Map, VariableDependency, FutureResolvable): def partial_resolve( self: TypeT, resolved_variables: Dict[Variable, Resolvable], + unresolved_variables: Dict[Variable, Argument], custom_functions: Dict[str, "VariableDependency"], ) -> TypeT | Resolvable: maybe_resolvable_keys, is_keys_resolvable = VariableDependency.try_partial_resolve( args=self.value.keys(), resolved_variables=resolved_variables, + unresolved_variables=unresolved_variables, custom_functions=custom_functions, ) maybe_resolvable_values, is_values_resolvable = VariableDependency.try_partial_resolve( args=self.value.values(), resolved_variables=resolved_variables, + unresolved_variables=unresolved_variables, custom_functions=custom_functions, ) diff --git a/src/ytdl_sub/script/types/syntax_tree.py b/src/ytdl_sub/script/types/syntax_tree.py index cc383f22..0be3637f 100644 --- a/src/ytdl_sub/script/types/syntax_tree.py +++ b/src/ytdl_sub/script/types/syntax_tree.py @@ -44,11 +44,13 @@ class SyntaxTree(VariableDependency): def partial_resolve( self: TypeT, resolved_variables: Dict[Variable, Resolvable], + unresolved_variables: Dict[Variable, Argument], custom_functions: Dict[str, VariableDependency], ) -> TypeT | Resolvable: maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve( args=self.ast, resolved_variables=resolved_variables, + unresolved_variables=unresolved_variables, custom_functions=custom_functions, ) @@ -100,6 +102,7 @@ class ResolvedSyntaxTree(SyntaxTree): def partial_resolve( self: TypeT, resolved_variables: Dict[Variable, Resolvable], + unresolved_variables: Dict[Variable, Argument], custom_functions: Dict[str, VariableDependency], ) -> TypeT | Resolvable: return self.ast[0] diff --git a/src/ytdl_sub/script/types/variable_dependency.py b/src/ytdl_sub/script/types/variable_dependency.py index 9425524a..5f53a4b1 100644 --- a/src/ytdl_sub/script/types/variable_dependency.py +++ b/src/ytdl_sub/script/types/variable_dependency.py @@ -143,6 +143,7 @@ class VariableDependency(ABC): def partial_resolve( self: TypeT, resolved_variables: Dict[Variable, Resolvable], + unresolved_variables: Dict[Variable, Argument], custom_functions: Dict[str, "VariableDependency"], ) -> TypeT | Resolvable: """ @@ -150,6 +151,8 @@ class VariableDependency(ABC): ---------- resolved_variables Lookup of variables that have been resolved + unresolved_variables + Lookup of variables that have not been resolved custom_functions Lookup of any custom functions that have been parsed @@ -248,6 +251,7 @@ class VariableDependency(ABC): cls, args: Iterable[Argument], resolved_variables: Dict[Variable, Resolvable], + unresolved_variables: Dict[Variable, Argument], custom_functions: Dict[str, "VariableDependency"], ) -> Tuple[List[Argument], bool]: maybe_resolvable_args: List[Resolvable | Argument] = [] @@ -265,7 +269,9 @@ class VariableDependency(ABC): elif isinstance(arg, VariableDependency): maybe_resolvable_args.append( arg.partial_resolve( - resolved_variables=resolved_variables, custom_functions=custom_functions + resolved_variables=resolved_variables, + unresolved_variables=unresolved_variables, + custom_functions=custom_functions, ) ) @@ -275,10 +281,14 @@ class VariableDependency(ABC): if arg not in resolved_variables: is_resolvable = False - maybe_resolvable_args.append(arg) + if arg in unresolved_variables: + maybe_resolvable_args.append(unresolved_variables[arg]) + else: + # Must be unresolvable + maybe_resolvable_args.append(arg) + else: + maybe_resolvable_args.append(arg) else: maybe_resolvable_args.append(arg) - if is_resolvable: - print("hmm") return maybe_resolvable_args, is_resolvable diff --git a/tests/unit/config/test_subscription.py b/tests/unit/config/test_subscription.py index 35720f71..a9b31425 100644 --- a/tests/unit/config/test_subscription.py +++ b/tests/unit/config/test_subscription.py @@ -617,6 +617,7 @@ def test_default_docker_config_and_subscriptions( unresolvable.add("sibling_metadata") out = default_subs[0].overrides.script.resolve_partial(unresolvable=unresolvable) - prev = ScriptUtils.to_native_script(out._variables["episode_file_name"]) + test1 = ScriptUtils.to_native_script(out._variables["episode_file_name"]) + test2 = ScriptUtils.to_native_script(out._variables["episode_file_path"]) print("hi")