From fb51295974a2784d726aa02d501db8f996c5b5e0 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Thu, 27 Nov 2025 11:26:41 -0800 Subject: [PATCH] Explicit resolved ASTs --- src/ytdl_sub/config/overrides.py | 5 +++++ src/ytdl_sub/script/script.py | 7 ++++++- src/ytdl_sub/script/types/syntax_tree.py | 14 ++++++++++++++ .../validators/string_formatter_validators.py | 1 + 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/ytdl_sub/config/overrides.py b/src/ytdl_sub/config/overrides.py index 8ed6990c..06ca8fce 100644 --- a/src/ytdl_sub/config/overrides.py +++ b/src/ytdl_sub/config/overrides.py @@ -158,10 +158,15 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable): script = entry.script unresolvable = entry.unresolvable + # Update the script internally so long as we are not supplying overrides + # that could alter the script with one-off state + update = function_overrides is None + try: return script.resolve_once( dict({"tmp_var": formatter.format_string}, **(function_overrides or {})), unresolvable=unresolvable, + update=update, )["tmp_var"] except ScriptVariableNotResolved as exc: raise StringFormattingException( diff --git a/src/ytdl_sub/script/script.py b/src/ytdl_sub/script/script.py index 45846278..e92e7fdd 100644 --- a/src/ytdl_sub/script/script.py +++ b/src/ytdl_sub/script/script.py @@ -10,6 +10,7 @@ from ytdl_sub.script.script_output import ScriptOutput from ytdl_sub.script.types.resolvable import BuiltInFunctionType from ytdl_sub.script.types.resolvable import Lambda from ytdl_sub.script.types.resolvable import Resolvable +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 @@ -273,7 +274,7 @@ class Script: def _update_internally(self, resolved_variables: Dict[str, Resolvable]) -> None: for variable_name, resolved in resolved_variables.items(): - self._variables[variable_name] = SyntaxTree(ast=[resolved]) + self._variables[variable_name] = ResolvedSyntaxTree(ast=[resolved]) def _recursive_get_unresolved_output_filter_variables( self, current_var: SyntaxTree, subset_to_resolve: Set[str], unresolvable: Set[Variable] @@ -522,6 +523,7 @@ class Script: variable_definitions: Dict[str, str], resolved: Optional[Dict[str, Resolvable]] = None, unresolvable: Optional[Set[str]] = None, + update: bool = False, ) -> Dict[str, Resolvable]: """ Given a new set of variable definitions, resolve them using the Script, but do not @@ -536,6 +538,8 @@ class Script: unresolvable Optional. Unresolvable variables that will be ignored in resolution, including all variables with a dependency to them. + update + Whether to update the script's state with resolved variables. Defaults to False. Returns ------- @@ -548,6 +552,7 @@ class Script: pre_resolved=resolved, unresolvable=unresolvable, output_filter=set(list(variable_definitions.keys())), + update=update, ).output finally: for name in variable_definitions.keys(): diff --git a/src/ytdl_sub/script/types/syntax_tree.py b/src/ytdl_sub/script/types/syntax_tree.py index d058852c..d1e5a6e3 100644 --- a/src/ytdl_sub/script/types/syntax_tree.py +++ b/src/ytdl_sub/script/types/syntax_tree.py @@ -50,3 +50,17 @@ class SyntaxTree(VariableDependency): if len(self.ast) == 1 and isinstance(self.ast[0], Resolvable): return self.ast[0] return None + + +@dataclass(frozen=True) +class ResolvedSyntaxTree(SyntaxTree): + def resolve( + self, + resolved_variables: Dict[Variable, Resolvable], + custom_functions: Dict[str, VariableDependency], + ) -> Resolvable: + return self.ast[0] + + @property + def maybe_resolvable(self) -> Optional[Resolvable]: + return self.ast[0] diff --git a/src/ytdl_sub/validators/string_formatter_validators.py b/src/ytdl_sub/validators/string_formatter_validators.py index 17436700..567f0f8d 100644 --- a/src/ytdl_sub/validators/string_formatter_validators.py +++ b/src/ytdl_sub/validators/string_formatter_validators.py @@ -246,6 +246,7 @@ def _validate_formatter( ) }, unresolvable=unresolvable, + update=True, ) except RuntimeException as exc: if isinstance(exc, ScriptVariableNotResolved) and is_static_formatter: