maybe better?

This commit is contained in:
Jesse Bannon 2023-12-08 00:03:43 -08:00
parent a7dcf49e00
commit fb58941ff5
5 changed files with 80 additions and 22 deletions

View file

@ -118,17 +118,13 @@ class Overrides(DictFormatterValidator, Scriptable):
The format_string after .format has been called The format_string after .format has been called
""" """
if entry: if entry:
script = copy.deepcopy(entry.script) script = entry.script
unresolvable = entry.unresolvable unresolvable = entry.unresolvable
else: else:
script = copy.deepcopy(self.script) script = self.script
unresolvable = self.unresolvable unresolvable = self.unresolvable
if function_overrides: if function_overrides:
script.add(function_overrides) script = copy.deepcopy(script).add(function_overrides)
return ( return str(script.is_resolvable(formatter.format_string, unresolvable=unresolvable))
script.add({"tmp_var": formatter.format_string})
.resolve(unresolvable=unresolvable)
.get_str("tmp_var")
)

View file

@ -190,6 +190,8 @@ class Preset(_PresetShell):
{source_var: "dummy_string" for source_var in self._source_variables} {source_var: "dummy_string" for source_var in self._source_variables}
) )
) )
# updates any resolved variables
_ = script.partial_build()
return script return script
@property @property
@ -209,7 +211,7 @@ class Preset(_PresetShell):
""" """
Contains actualized script which should hold all Override variables Contains actualized script which should hold all Override variables
""" """
return self._script_builder_with_added_variables.partial_build(update=True) return self._script_builder_with_added_variables.partial_build()
@property @property
def _script(self) -> Script: def _script(self) -> Script:
@ -252,19 +254,27 @@ class Preset(_PresetShell):
) )
) )
def __validate_override_string_formatter_validator( @functools.cache
def _get_unresolvable_variables(
self, self,
formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator], formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator],
) -> None: ) -> Optional[Set[str]]:
unresolvable = ( unresolvable = (
set([VARIABLES.entry_metadata.variable_name] + list(self._added_variables.keys())) set([VARIABLES.entry_metadata.variable_name] + list(self._added_variables.keys()))
if isinstance(formatter_validator, OverridesStringFormatterValidator) if isinstance(formatter_validator, OverridesStringFormatterValidator)
else None else None
) )
return unresolvable
def __validate_override_string_formatter_validator(
self,
formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator],
) -> None:
try: try:
self._script.add({"tmp_var": formatter_validator.format_string}).resolve( self._script.is_resolvable(
unresolvable=unresolvable formatter_validator.format_string,
).get("tmp_var") unresolvable=self._get_unresolvable_variables(formatter_validator),
)
except VariableDoesNotExist as exc: except VariableDoesNotExist as exc:
raise StringFormattingVariableNotFoundException(exc) from exc raise StringFormattingVariableNotFoundException(exc) from exc

View file

@ -261,7 +261,7 @@ class MultiUrlValidator(OptionsValidator):
for collection_url in self.urls.list: for collection_url in self.urls.list:
script.add(collection_url.variables.dict_with_format_strings) script.add(collection_url.variables.dict_with_format_strings)
resolved_script = script.partial_build(update=True) resolved_script = script.partial_build()
# Ensure at least URL is non-empty # Ensure at least URL is non-empty
has_non_empty_url = False has_non_empty_url = False

View file

@ -246,11 +246,12 @@ class Script:
for variable_name, resolved in resolved_variables.items(): for variable_name, resolved in resolved_variables.items():
self._variables[variable_name] = SyntaxTree(ast=[resolved]) self._variables[variable_name] = SyntaxTree(ast=[resolved])
def resolve( def _resolve(
self, self,
resolved: Optional[Dict[str, Resolvable]] = None, resolved: Optional[Dict[str, Resolvable]] = None,
unresolvable: Optional[Set[str]] = None, unresolvable: Optional[Set[str]] = None,
update: bool = False, update: bool = False,
output_filter: Optional[Set[str]] = None,
) -> ScriptOutput: ) -> ScriptOutput:
""" """
Parameters Parameters
@ -268,9 +269,17 @@ class Script:
------- -------
Dict of resolved values Dict of resolved values
""" """
resolved: Dict[Variable, Resolvable] = { resolved: Dict[Variable, Resolvable] = dict(
Variable(name): value for name, value in (resolved or {}).items() # include all current variables that are resolvable
} {
Variable(name): ast.resolvable
for name, ast in self._variables.items()
if ast.resolvable is not None
},
# add explicit defined resolved variables
**{Variable(name): value for name, value in (resolved or {}).items()},
)
unresolvable: Set[Variable] = {Variable(name) for name in (unresolvable or {})} unresolvable: Set[Variable] = {Variable(name) for name in (unresolvable or {})}
unresolved: Dict[Variable, SyntaxTree] = { unresolved: Dict[Variable, SyntaxTree] = {
Variable(name): ast Variable(name): ast
@ -278,6 +287,8 @@ class Script:
if Variable(name) not in set(resolved.keys()).union(unresolvable) if Variable(name) not in set(resolved.keys()).union(unresolvable)
} }
tmp = 0
while unresolved: while unresolved:
unresolved_count: int = len(unresolved) unresolved_count: int = len(unresolved)
@ -308,8 +319,27 @@ class Script:
if update: if update:
self._update_internally(resolved_variables=resolved_variables) self._update_internally(resolved_variables=resolved_variables)
if output_filter:
return ScriptOutput(
{
name: resolvable
for name, resolvable in resolved_variables.items()
if name in output_filter
}
)
return ScriptOutput(resolved_variables) return ScriptOutput(resolved_variables)
def resolve(
self,
resolved: Optional[Dict[str, Resolvable]] = None,
unresolvable: Optional[Set[str]] = None,
update: bool = False,
) -> ScriptOutput:
return self._resolve(
resolved=resolved, unresolvable=unresolvable, update=update, output_filter=None
)
def add(self, variables: Dict[str, str]) -> "Script": def add(self, variables: Dict[str, str]) -> "Script":
for variable_name, variable_definition in variables.items(): for variable_name, variable_definition in variables.items():
self._variables[variable_name] = parse( self._variables[variable_name] = parse(
@ -321,6 +351,21 @@ class Script:
self._validate() self._validate()
return self return self
def is_resolvable(
self,
variable_definition: str,
resolved: Optional[Dict[str, Resolvable]] = None,
unresolvable: Optional[Set[str]] = None,
) -> Resolvable:
try:
self.add({"tmp_var": variable_definition})
return self._resolve(
resolved=resolved, unresolvable=unresolvable, output_filter={"tmp_var"}
).get("tmp_var")
finally:
if "tmp_var" in self._variables:
del self._variables["tmp_var"]
def get(self, variable_name: str) -> Resolvable: def get(self, variable_name: str) -> Resolvable:
if variable_name not in self._variables: if variable_name not in self._variables:
raise RuntimeException( raise RuntimeException(
@ -419,7 +464,7 @@ class ScriptBuilder:
script._validate() script._validate()
return script return script
def partial_build(self, update: bool = False) -> Script: def partial_build(self) -> Script:
missing_variables, missing_functions = self._missing_metadata missing_variables, missing_functions = self._missing_metadata
maybe_resolvable_variables: Dict[str, SyntaxTree] = { maybe_resolvable_variables: Dict[str, SyntaxTree] = {
name: variable name: variable
@ -435,8 +480,9 @@ class ScriptBuilder:
script = self._build( script = self._build(
variables=maybe_resolvable_variables, functions=maybe_resolvable_functions variables=maybe_resolvable_variables, functions=maybe_resolvable_functions
) )
if update: # Update internal variables with anything that is resolved
script.resolve(update=True) for variable_name, variable_output in script.resolve(update=True).output.items():
self._variables[variable_name] = SyntaxTree([variable_output])
return script return script
def build(self) -> Script: def build(self) -> Script:

View file

@ -5,6 +5,7 @@ from typing import Optional
from typing import Tuple from typing import Tuple
import pytest import pytest
import yappi
from expected_download import assert_expected_downloads from expected_download import assert_expected_downloads
from expected_transaction_log import assert_transaction_log_matches from expected_transaction_log import assert_transaction_log_matches
@ -149,6 +150,9 @@ class TestPrebuiltTVShowPresets:
is_youtube_channel: bool, is_youtube_channel: bool,
is_many_urls: bool, is_many_urls: bool,
): ):
# yappi.set_clock_type("wall") # Use set_clock_type("wall") for wall time
# yappi.start()
expected_summary_name = "unit/{}/{}/is_yt_{}{}".format( expected_summary_name = "unit/{}/{}/is_yt_{}{}".format(
media_player_preset, media_player_preset,
tv_show_structure_preset, tv_show_structure_preset,
@ -218,6 +222,8 @@ class TestPrebuiltTVShowPresets:
) )
reformatted_transaction_log = reformatted_subscription.update_with_info_json(dry_run=False) reformatted_transaction_log = reformatted_subscription.update_with_info_json(dry_run=False)
# yappi.get_func_stats().print_all()
assert_transaction_log_matches( assert_transaction_log_matches(
output_directory=output_directory, output_directory=output_directory,
transaction_log=reformatted_transaction_log, transaction_log=reformatted_transaction_log,