[BACKEND] partial resolve support

This commit is contained in:
Jesse Bannon 2026-01-04 00:08:43 -08:00
parent b2056bec5d
commit e86e414ad9
4 changed files with 118 additions and 0 deletions

View file

@ -11,6 +11,7 @@ from ytdl_sub.script.types.resolvable import NonHashable
from ytdl_sub.script.types.resolvable import Resolvable from ytdl_sub.script.types.resolvable import Resolvable
from ytdl_sub.script.types.resolvable import ResolvableToJson from ytdl_sub.script.types.resolvable import ResolvableToJson
from ytdl_sub.script.types.variable import Variable from ytdl_sub.script.types.variable import Variable
from ytdl_sub.script.types.variable_dependency import TypeT
from ytdl_sub.script.types.variable_dependency import VariableDependency from ytdl_sub.script.types.variable_dependency import VariableDependency
@ -47,6 +48,25 @@ class UnresolvedArray(_Array, VariableDependency, FutureResolvable):
] ]
) )
def partial_resolve(
self,
resolved_variables: Dict[Variable, Resolvable],
custom_functions: Dict[str, "VariableDependency"],
) -> "UnresolvedArray" | Resolvable:
maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve(
args=self.value,
resolved_variables=resolved_variables,
custom_functions=custom_functions,
)
if is_resolvable:
return self.resolve(
resolved_variables=resolved_variables,
custom_functions=custom_functions,
)
return UnresolvedArray(value=maybe_resolvable_values)
def future_resolvable_type(self) -> Type[Resolvable]: def future_resolvable_type(self) -> Type[Resolvable]:
return Array return Array

View file

@ -22,6 +22,7 @@ from ytdl_sub.script.types.resolvable import ReturnableArgumentA
from ytdl_sub.script.types.resolvable import ReturnableArgumentB from ytdl_sub.script.types.resolvable import ReturnableArgumentB
from ytdl_sub.script.types.variable import FunctionArgument 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.types.variable_dependency import TypeT
from ytdl_sub.script.types.variable_dependency import VariableDependency from ytdl_sub.script.types.variable_dependency import VariableDependency
from ytdl_sub.script.utils.exception_formatters import FunctionArgumentsExceptionFormatter from ytdl_sub.script.utils.exception_formatters import FunctionArgumentsExceptionFormatter
from ytdl_sub.script.utils.exceptions import UNREACHABLE from ytdl_sub.script.utils.exceptions import UNREACHABLE
@ -38,6 +39,25 @@ class Function(FunctionType, VariableDependency, ABC):
def _iterable_arguments(self) -> List[Argument]: def _iterable_arguments(self) -> List[Argument]:
return self.args return self.args
def partial_resolve(
self: TypeT,
resolved_variables: Dict[Variable, Resolvable],
custom_functions: Dict[str, "VariableDependency"],
) -> TypeT | Resolvable:
maybe_resolvable_values, is_resolvable = VariableDependency.try_partial_resolve(
args=self.value,
resolved_variables=resolved_variables,
custom_functions=custom_functions,
)
if is_resolvable:
return self.resolve(
resolved_variables=resolved_variables,
custom_functions=custom_functions,
)
return BuiltInFunction(name=self.name, args=maybe_resolvable_values)
class CustomFunction(Function, NamedCustomFunction): class CustomFunction(Function, NamedCustomFunction):
def resolve( def resolve(

View file

@ -55,6 +55,31 @@ class UnresolvedMap(_Map, VariableDependency, FutureResolvable):
return Map(output) return Map(output)
def partial_resolve(
self,
resolved_variables: Dict[Variable, Resolvable],
custom_functions: Dict[str, "VariableDependency"],
) -> "UnresolvedMap" | Resolvable:
maybe_resolvable_keys, is_keys_resolvable = VariableDependency.try_partial_resolve(
args=self.value.keys(),
resolved_variables=resolved_variables,
custom_functions=custom_functions,
)
maybe_resolvable_values, is_values_resolvable = VariableDependency.try_partial_resolve(
args=self.value.values(),
resolved_variables=resolved_variables,
custom_functions=custom_functions,
)
if is_keys_resolvable and is_values_resolvable:
return self.resolve(
resolved_variables=resolved_variables,
custom_functions=custom_functions,
)
return UnresolvedMap(value=dict(zip(maybe_resolvable_keys, maybe_resolvable_values)))
def future_resolvable_type(self) -> Type[Resolvable]: def future_resolvable_type(self) -> Type[Resolvable]:
return Map return Map

View file

@ -5,6 +5,7 @@ from typing import Dict
from typing import Iterable from typing import Iterable
from typing import List from typing import List
from typing import Set from typing import Set
from typing import Tuple
from typing import Type from typing import Type
from typing import TypeVar from typing import TypeVar
from typing import final from typing import final
@ -138,6 +139,25 @@ class VariableDependency(ABC):
Resolved value Resolved value
""" """
@abstractmethod
def partial_resolve(
self: TypeT,
resolved_variables: Dict[Variable, Resolvable],
custom_functions: Dict[str, "VariableDependency"],
) -> TypeT | Resolvable:
"""
Parameters
----------
resolved_variables
Lookup of variables that have been resolved
custom_functions
Lookup of any custom functions that have been parsed
Returns
-------
Either a fully resolved value or partially resolved value of the same type.
"""
@classmethod @classmethod
def _resolve_argument_type( def _resolve_argument_type(
cls, cls,
@ -222,3 +242,36 @@ class VariableDependency(ABC):
): ):
return True return True
return len(self.variables.intersection(variables)) > 0 return len(self.variables.intersection(variables)) > 0
@classmethod
def try_partial_resolve(
cls,
args: Iterable[Argument],
resolved_variables: Dict[Variable, Resolvable],
custom_functions: Dict[str, "VariableDependency"],
) -> Tuple[List[Argument], bool]:
maybe_resolvable_args: List[Resolvable | Argument] = []
is_resolvable = True
for arg in args:
if isinstance(arg, Lambda) and arg.value in custom_functions:
maybe_resolvable_args.append(arg)
if not custom_functions[arg.value].is_subset_of(
variables=resolved_variables,
custom_function_definitions=custom_functions,
):
is_resolvable = False
elif isinstance(arg, VariableDependency):
maybe_resolvable_args.append(
arg.partial_resolve(
resolved_variables=resolved_variables, custom_functions=custom_functions
)
)
if not isinstance(maybe_resolvable_args[-1], Resolvable):
is_resolvable = False
else:
maybe_resolvable_args.append(arg)
return maybe_resolvable_args, is_resolvable