From 7b45d8d4fe3e5ed6f47dfbb77240e36dbf420c16 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Tue, 30 Dec 2025 15:36:46 -0800 Subject: [PATCH] fixed lint, tests --- src/ytdl_sub/script/parser.py | 4 +- src/ytdl_sub/script/script.py | 12 ++-- src/ytdl_sub/script/utils/name_validation.py | 2 +- .../validators/string_formatter_validators.py | 8 +-- tests/unit/config/test_subscription.py | 60 ++++++++++++++++++- 5 files changed, 69 insertions(+), 17 deletions(-) diff --git a/src/ytdl_sub/script/parser.py b/src/ytdl_sub/script/parser.py index 23b650e5..73dbb3f4 100644 --- a/src/ytdl_sub/script/parser.py +++ b/src/ytdl_sub/script/parser.py @@ -31,8 +31,8 @@ from ytdl_sub.script.utils.exceptions import InvalidSyntaxException from ytdl_sub.script.utils.exceptions import InvalidVariableName from ytdl_sub.script.utils.exceptions import UserException from ytdl_sub.script.utils.exceptions import VariableDoesNotExist -from ytdl_sub.script.utils.name_validation import function_name from ytdl_sub.script.utils.name_validation import is_function +from ytdl_sub.script.utils.name_validation import to_function_name from ytdl_sub.script.utils.name_validation import validate_variable_name # pylint: disable=invalid-name @@ -147,7 +147,7 @@ class _Parser: self._text = text self._name = name if name and is_function(name): - self._name = function_name(name) + self._name = to_function_name(name) self._custom_function_names = custom_function_names self._variable_names = variable_names diff --git a/src/ytdl_sub/script/script.py b/src/ytdl_sub/script/script.py index db340f8e..67eeeaf1 100644 --- a/src/ytdl_sub/script/script.py +++ b/src/ytdl_sub/script/script.py @@ -20,9 +20,9 @@ from ytdl_sub.script.utils.exceptions import IncompatibleFunctionArguments from ytdl_sub.script.utils.exceptions import InvalidCustomFunctionArguments from ytdl_sub.script.utils.exceptions import RuntimeException from ytdl_sub.script.utils.exceptions import ScriptVariableNotResolved -from ytdl_sub.script.utils.name_validation import function_name from ytdl_sub.script.utils.name_validation import is_function from ytdl_sub.script.utils.name_validation import to_function_definition_name +from ytdl_sub.script.utils.name_validation import to_function_name from ytdl_sub.script.utils.name_validation import validate_variable_name from ytdl_sub.script.utils.type_checking import FunctionSpec @@ -226,7 +226,7 @@ class Script: def __init__(self, script: Dict[str, str]): function_names: Set[str] = { - function_name(name) for name in script.keys() if is_function(name) + to_function_name(name) for name in script.keys() if is_function(name) } variable_names: Set[str] = { validate_variable_name(name) for name in script.keys() if not is_function(name) @@ -235,9 +235,9 @@ class Script: self._functions: Dict[str, SyntaxTree] = { # custom_function_name must be passed to properly type custom function # arguments uniquely if they're nested (i.e. $0 to $custom_func___0) - function_name(function_key): parse( + to_function_name(function_key): parse( text=function_value, - name=function_name(function_key), + name=to_function_name(function_key), custom_function_names=function_names, variable_names=variable_names, ) @@ -470,7 +470,7 @@ class Script: added_variables_to_validate: Set[str] = set() functions_to_add = { - function_name(name): definition + to_function_name(name): definition for name, definition in variables.items() if is_function(name) } @@ -522,7 +522,7 @@ class Script: added_variables_to_validate: Set[str] = set() functions_to_add = { - function_name(name): definition + to_function_name(name): definition for name, definition in variables.items() if is_function(name) } diff --git a/src/ytdl_sub/script/utils/name_validation.py b/src/ytdl_sub/script/utils/name_validation.py index 7e212cd7..5b2597ce 100644 --- a/src/ytdl_sub/script/utils/name_validation.py +++ b/src/ytdl_sub/script/utils/name_validation.py @@ -67,7 +67,7 @@ def is_function(override_name: str): return override_name.startswith("%") -def function_name(function_key: str) -> str: +def to_function_name(function_key: str) -> str: """ Drop the % in %custom_function """ diff --git a/src/ytdl_sub/validators/string_formatter_validators.py b/src/ytdl_sub/validators/string_formatter_validators.py index a5e40f6f..19a59344 100644 --- a/src/ytdl_sub/validators/string_formatter_validators.py +++ b/src/ytdl_sub/validators/string_formatter_validators.py @@ -20,6 +20,8 @@ from ytdl_sub.validators.validators import LiteralDictValidator from ytdl_sub.validators.validators import StringValidator from ytdl_sub.validators.validators import Validator +# pylint: disable=protected-access + class StringFormatterValidator(StringValidator): """ @@ -218,10 +220,8 @@ def to_variable_dependency_format_string(script: Script, parsed_format_string: S dummy_format_string = "" for var in parsed_format_string.variables: dummy_format_string += f"{{ {var.name} }}" - # pylint: disable=protected-access for variable_dependency in script._variables[var.name].variables: dummy_format_string += f"{{ {variable_dependency.name} }}" - # pylint: enable=protected-access return dummy_format_string @@ -293,7 +293,6 @@ def validate_formatters( if isinstance(validator, DictValidator): resolved_dict[validator.leaf_name] = {} - # pylint: disable=protected-access # Usage of protected variables in other validators is fine. The reason to keep # them protected is for readability when using them in subscriptions. for validator_value in validator._validator_dict.values(): @@ -302,7 +301,6 @@ def validate_formatters( unresolved_variables=unresolved_variables, validator=validator_value, ) - # pylint: enable=protected-access elif isinstance(validator, ListValidator): resolved_dict[validator.leaf_name] = [] for list_value in validator.list: @@ -328,8 +326,6 @@ def validate_formatters( formatter_validator=validator_value, ) else: - # pylint: disable=protected-access resolved_dict[validator.leaf_name] = validator._value - # pylint: enable=protected-access return resolved_dict diff --git a/tests/unit/config/test_subscription.py b/tests/unit/config/test_subscription.py index b007e5c2..780c2527 100644 --- a/tests/unit/config/test_subscription.py +++ b/tests/unit/config/test_subscription.py @@ -6,6 +6,7 @@ from typing import Dict from unittest.mock import patch import pytest +import yaml from ytdl_sub.config.config_file import ConfigFile from ytdl_sub.plugins.nfo_tags import NfoTagsOptions @@ -540,13 +541,69 @@ def test_music_video_subscriptions(default_config: ConfigFile, music_video_subsc assert gnr.get("url2").native == "https://www.youtube.com/watch?v=OldpIhHPsbs" -def test_default_docker_config_and_subscriptions(docker_default_subscription_path: Path): +def test_default_docker_config_and_subscriptions(docker_default_subscription_path: Path, output_directory: str): default_config = ConfigFile.from_file_path("docker/root/defaults/config.yaml") default_subs = Subscription.from_file_path( config=default_config, subscription_path=docker_default_subscription_path ) assert len(default_subs) == 1 + resolved_yaml_as_json = yaml.safe_load(default_subs[0].resolved_yaml()) + + # Since this creates random values, ignore it for this test + assert "throttle_protection" in resolved_yaml_as_json + del resolved_yaml_as_json["throttle_protection"] + + assert resolved_yaml_as_json == { + "chapters": { + "allow_chapters_from_comments": False, + "embed_chapters": True, + "enable": "True", + "force_key_frames": False, + }, + "date_range": {"breaks": "True", "enable": "True", "type": "upload_date"}, + "download": [ + { + "download_reverse": "True", + "include_sibling_metadata": False, + "playlist_thumbnails": [ + {"name": "{avatar_uncropped_thumbnail_file_name}", "uid": "avatar_uncropped"}, + {"name": "{banner_uncropped_thumbnail_file_name}", "uid": "banner_uncropped"}, + ], + "source_thumbnails": [ + {"name": "{avatar_uncropped_thumbnail_file_name}", "uid": "avatar_uncropped"}, + {"name": "{banner_uncropped_thumbnail_file_name}", "uid": "banner_uncropped"}, + ], + "url": "https://www.youtube.com/@novapbs", + "variables": {}, + "webpage_url": "{modified_webpage_url}", + "ytdl_options": {}, + } + ], + "file_convert": {"convert_to": "mp4", "convert_with": "yt-dlp", "enable": "True"}, + "format": "(bv*[ext=mp4][vcodec~='^((he|a)vc|h26[45])']+ba[ext=m4a]) / (bv[ext=mp4]*+ba[ext=m4a]/b)", + "output_options": { + "download_archive_name": ".ytdl-sub-NOVA PBS-download-archive.json", + "file_name": "{episode_file_path}.{ext}", + "info_json_name": "{episode_file_path}.{info_json_ext}", + "keep_files_date_eval": "{episode_date_standardized}", + "maintain_download_archive": True, + "output_directory": f"{output_directory}/NOVA PBS", + "preserve_mtime": False, + "thumbnail_name": "{thumbnail_file_name}", + }, + "video_tags": { + "contentRating": "{episode_content_rating}", + "date": "{episode_date_standardized}", + "episode_id": "{episode_number}", + "genre": "{tv_show_genre}", + "show": "{tv_show_name}", + "synopsis": "{episode_plot}", + "title": "{episode_title}", + "year": "{episode_year}", + }, + } + def test_tv_show_resolved_yaml(config_file: ConfigFile, tv_show_subscriptions_path: Path): subs = Subscription.from_file_path( @@ -555,4 +612,3 @@ def test_tv_show_resolved_yaml(config_file: ConfigFile, tv_show_subscriptions_pa assert len(subs) == 8 yaml_out = subs[0].resolved_yaml() - print(yaml_out)