From cfcaa1878ac8e29a15b8eb272d16e330471947dc Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Sat, 27 Dec 2025 16:27:41 -0800 Subject: [PATCH] nearly there, need url support --- .../config/validators/variable_validation.py | 14 +-- .../subscriptions/base_subscription.py | 20 +-- .../validators/string_formatter_validators.py | 3 +- tests/unit/config/test_preset.py | 95 -------------- tests/unit/config/test_subscription.py | 16 +-- .../config/test_subscription_validation.py | 116 ++++++++++++++++++ 6 files changed, 143 insertions(+), 121 deletions(-) create mode 100644 tests/unit/config/test_subscription_validation.py diff --git a/src/ytdl_sub/config/validators/variable_validation.py b/src/ytdl_sub/config/validators/variable_validation.py index ba2ea5bd..b6b4439d 100644 --- a/src/ytdl_sub/config/validators/variable_validation.py +++ b/src/ytdl_sub/config/validators/variable_validation.py @@ -15,7 +15,6 @@ from ytdl_sub.config.validators.options import OptionsValidator from ytdl_sub.downloaders.url.validators import MultiUrlValidator from ytdl_sub.entries.variables.override_variables import REQUIRED_OVERRIDE_VARIABLE_NAMES from ytdl_sub.script.script import Script -from ytdl_sub.script.script import _is_function from ytdl_sub.script.utils.exceptions import RuntimeException from ytdl_sub.utils.scriptable import BASE_SCRIPT from ytdl_sub.validators.string_formatter_validators import to_variable_dependency_format_string @@ -45,18 +44,15 @@ def _add_dummy_variables(variables: Iterable[str]) -> Dict[str, str]: def _add_dummy_overrides(overrides: Overrides) -> Dict[str, str]: # Have the dummy override variable contain all variable deps that it uses in the string dummy_overrides: Dict[str, str] = {} - for override_name, format_string in overrides.dict_with_format_strings.items(): - - if _is_function(override_name): - continue - + for override_name in overrides.script.variable_names: try: # Attempt to get the resolved version, which will only happen # if it does not have any dependencies to the entry value = overrides.script.get(override_name).native except RuntimeException: value = to_variable_dependency_format_string( - script=overrides.script, parsed_format_string=overrides.script._variables[override_name] + script=overrides.script, + parsed_format_string=overrides.script._variables[override_name], ) dummy_overrides[override_name] = value @@ -130,9 +126,9 @@ class VariableValidation: # copy the script and mock entry variables self.script = copy.deepcopy(self.overrides.script) self.script.add( - variables=_add_dummy_overrides(overrides=self.overrides) + variables=_DUMMY_ENTRY_VARIABLES | _add_dummy_variables(variables=plugin_variables) - | _DUMMY_ENTRY_VARIABLES + | _add_dummy_overrides(overrides=self.overrides) ) return self diff --git a/src/ytdl_sub/subscriptions/base_subscription.py b/src/ytdl_sub/subscriptions/base_subscription.py index b89911f0..656e98ed 100644 --- a/src/ytdl_sub/subscriptions/base_subscription.py +++ b/src/ytdl_sub/subscriptions/base_subscription.py @@ -15,6 +15,7 @@ from ytdl_sub.utils.exceptions import SubscriptionPermissionError from ytdl_sub.utils.file_handler import FileHandler from ytdl_sub.utils.file_handler import FileHandlerTransactionLog from ytdl_sub.utils.logger import Logger +from ytdl_sub.utils.yaml import dump_yaml from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive logger = Logger.get("subscription") @@ -77,6 +78,14 @@ class BaseSubscription(ABC): } ) + # Validate after adding the subscription name + self._validated_dict = VariableValidation( + overrides=self.overrides, + downloader_options=self.downloader_options, + output_options=self.output_options, + plugins=self.plugins, + ).ensure_proper_usage() + self._enhanced_download_archive: Optional[EnhancedDownloadArchive] = ( _initialize_download_archive( output_options=self.output_options, @@ -103,13 +112,6 @@ class BaseSubscription(ABC): f"{self.output_directory}" ) - self._validated_dict = VariableValidation( - overrides=self.overrides, - downloader_options=self.downloader_options, - output_options=self.output_options, - plugins=self.plugins, - ).ensure_proper_usage() - @property def download_archive(self) -> EnhancedDownloadArchive: """ @@ -254,10 +256,10 @@ class BaseSubscription(ABC): """ return self._preset_options.yaml - def resolved_yaml(self): + def resolved_yaml(self) -> str: """ Returns ------- Human-readable, condensed YAML definition of the subscription. """ - return self._validated_dict + return dump_yaml(self._validated_dict) diff --git a/src/ytdl_sub/validators/string_formatter_validators.py b/src/ytdl_sub/validators/string_formatter_validators.py index 703568f9..9059372f 100644 --- a/src/ytdl_sub/validators/string_formatter_validators.py +++ b/src/ytdl_sub/validators/string_formatter_validators.py @@ -8,9 +8,10 @@ from ytdl_sub.entries.script.variable_definitions import VARIABLES from ytdl_sub.script.parser import parse from ytdl_sub.script.script import Script from ytdl_sub.script.types.syntax_tree import SyntaxTree -from ytdl_sub.script.utils.exceptions import RuntimeException, UserThrownRuntimeError +from ytdl_sub.script.utils.exceptions import RuntimeException from ytdl_sub.script.utils.exceptions import ScriptVariableNotResolved from ytdl_sub.script.utils.exceptions import UserException +from ytdl_sub.script.utils.exceptions import UserThrownRuntimeError from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException from ytdl_sub.utils.script import ScriptUtils from ytdl_sub.validators.validators import DictValidator diff --git a/tests/unit/config/test_preset.py b/tests/unit/config/test_preset.py index 939eab92..cd4f2b17 100644 --- a/tests/unit/config/test_preset.py +++ b/tests/unit/config/test_preset.py @@ -6,7 +6,6 @@ import pytest from ytdl_sub.config.preset import Preset from ytdl_sub.plugins.nfo_tags import NfoTagsOptions -from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException from ytdl_sub.utils.exceptions import ValidationException @@ -157,100 +156,6 @@ class TestPreset: }, ) - def test_preset_error__source_variable_does_not_exist( - self, config_file, output_options, youtube_video - ): - with pytest.raises( - StringFormattingVariableNotFoundException, - match="contains the following variables that do not exist: dne_var", - ): - _ = Preset( - config=config_file, - name="test", - value={ - "download": youtube_video, - "output_options": {"output_directory": "dir", "file_name": "{dne_var}"}, - }, - ) - - def test_preset_error__override_variable_does_not_exist( - self, config_file, output_options, youtube_video - ): - with pytest.raises( - StringFormattingVariableNotFoundException, - match="contains the following variables that do not exist: dne_var", - ): - _ = Preset( - config=config_file, - name="test", - value={ - "download": youtube_video, - "output_options": {"output_directory": "{dne_var}", "file_name": "file"}, - }, - ) - - def test_preset_error__dict_source_variable_does_not_exist( - self, config_file, output_options, youtube_video - ): - with pytest.raises( - StringFormattingVariableNotFoundException, - match="contains the following variables that do not exist: dne_var", - ): - _ = Preset( - config=config_file, - name="test", - value={ - "download": youtube_video, - "output_options": {"output_directory": "dir", "file_name": "file"}, - "nfo_tags": { - "nfo_name": "the nfo name", - "nfo_root": "the root", - "tags": {"tag_a": "{dne_var}"}, - }, - }, - ) - - def test_preset_error__dict_override_variable_does_not_exist( - self, config_file, output_options, youtube_video - ): - with pytest.raises( - StringFormattingVariableNotFoundException, - match="contains the following variables that do not exist: dne_var", - ): - _ = Preset( - config=config_file, - name="test", - value={ - "download": youtube_video, - "output_options": output_options, - "output_directory_nfo_tags": { - "nfo_name": "the nfo name", - "nfo_root": "the root", - "tags": {"tag_a": "{dne_var}"}, - }, - }, - ) - - def test_preset_error__dict_override_variable_not_static( - self, config_file, output_options, youtube_video - ): - with pytest.raises( - StringFormattingVariableNotFoundException, - match="static formatters must contain variables that " - "have no dependency to entry variables", - ): - _ = Preset( - config=config_file, - name="test", - value={ - "download": youtube_video, - "output_options": { - "output_directory": "{title}", - "file_name": "{uid}", - }, - }, - ) - def test_preset_with_multi_url__contains_empty_url(self, config_file, output_options): _ = Preset( config=config_file, diff --git a/tests/unit/config/test_subscription.py b/tests/unit/config/test_subscription.py index d3f7aee7..55362227 100644 --- a/tests/unit/config/test_subscription.py +++ b/tests/unit/config/test_subscription.py @@ -548,10 +548,12 @@ def test_default_docker_config_and_subscriptions(docker_default_subscription_pat assert len(default_subs) == 1 -def test_tv_show_resolved_yaml(config_file: ConfigFile, tv_show_subscriptions_path: Path): - subs = Subscription.from_file_path( - config=config_file, subscription_path=tv_show_subscriptions_path - ) - - assert len(subs) == 8 - assert subs[0].resolved_yaml() == {} +# +# def test_tv_show_resolved_yaml(config_file: ConfigFile, tv_show_subscriptions_path: Path): +# subs = Subscription.from_file_path( +# config=config_file, subscription_path=tv_show_subscriptions_path +# ) +# +# assert len(subs) == 8 +# yaml_out = subs[0].resolved_yaml() +# assert yaml_out == "nope" diff --git a/tests/unit/config/test_subscription_validation.py b/tests/unit/config/test_subscription_validation.py new file mode 100644 index 00000000..fb270d07 --- /dev/null +++ b/tests/unit/config/test_subscription_validation.py @@ -0,0 +1,116 @@ +import pytest + +from ytdl_sub.config.preset import Preset +from ytdl_sub.subscriptions.subscription import Subscription +from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException + + +class TestSubscriptionValidation: + def test_preset_error__source_variable_does_not_exist( + self, config_file, output_options, youtube_video + ): + with pytest.raises( + StringFormattingVariableNotFoundException, + match="contains the following variables that do not exist: dne_var", + ): + _ = Subscription.from_preset( + preset=Preset( + config=config_file, + name="test", + value={ + "download": youtube_video, + "output_options": {"output_directory": "dir", "file_name": "{dne_var}"}, + }, + ), + config=config_file, + ) + + def test_preset_error__override_variable_does_not_exist( + self, config_file, output_options, youtube_video + ): + with pytest.raises( + StringFormattingVariableNotFoundException, + match="contains the following variables that do not exist: dne_var", + ): + _ = Subscription.from_preset( + preset=Preset( + config=config_file, + name="test", + value={ + "download": youtube_video, + "output_options": {"output_directory": "{dne_var}", "file_name": "file"}, + }, + ), + config=config_file, + ) + + def test_preset_error__dict_source_variable_does_not_exist( + self, config_file, output_options, youtube_video + ): + with pytest.raises( + StringFormattingVariableNotFoundException, + match="contains the following variables that do not exist: dne_var", + ): + _ = Subscription.from_preset( + preset=Preset( + config=config_file, + name="test", + value={ + "download": youtube_video, + "output_options": {"output_directory": "dir", "file_name": "file"}, + "nfo_tags": { + "nfo_name": "the nfo name", + "nfo_root": "the root", + "tags": {"tag_a": "{dne_var}"}, + }, + }, + ), + config=config_file, + ) + + def test_preset_error__dict_override_variable_does_not_exist( + self, config_file, output_options, youtube_video + ): + with pytest.raises( + StringFormattingVariableNotFoundException, + match="contains the following variables that do not exist: dne_var", + ): + _ = Subscription.from_preset( + preset=Preset( + config=config_file, + name="test", + value={ + "download": youtube_video, + "output_options": output_options, + "output_directory_nfo_tags": { + "nfo_name": "the nfo name", + "nfo_root": "the root", + "tags": {"tag_a": "{dne_var}"}, + }, + }, + ), + config=config_file, + ) + + def test_preset_error__dict_override_variable_not_static( + self, config_file, output_options, youtube_video + ): + with pytest.raises( + StringFormattingVariableNotFoundException, + match="static formatters must contain variables that " + "have no dependency to entry variables", + ): + _ = Subscription.from_preset( + preset=Preset( + config=config_file, + name="test", + value={ + "download": youtube_video, + "output_options": { + "output_directory": "{title}", + "file_name": "{uid}", + }, + }, + ), + config=config_file, + )