From 7f8fc5903b04b8f652c203ec39d8ccc6e695fa97 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Sun, 28 Aug 2022 00:11:53 -0700 Subject: [PATCH] fix generics --- src/ytdl_sub/plugins/nfo_tags.py | 45 +++++++++++++++---- .../plugins/output_directory_nfo_tags.py | 30 +++++++++++-- tests/e2e/plugins/test_nfo_tags.py | 14 ++++++ 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/ytdl_sub/plugins/nfo_tags.py b/src/ytdl_sub/plugins/nfo_tags.py index f973961e..dcc370db 100644 --- a/src/ytdl_sub/plugins/nfo_tags.py +++ b/src/ytdl_sub/plugins/nfo_tags.py @@ -5,6 +5,7 @@ from typing import Dict from typing import Generic from typing import Optional from typing import Type +from typing import TypeVar from ytdl_sub.entries.entry import Entry from ytdl_sub.plugins.plugin import Plugin @@ -22,18 +23,20 @@ from ytdl_sub.validators.string_formatter_validators import DictFormatterValidat from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator from ytdl_sub.validators.validators import BoolValidator +TSharedNfoTagsValidator = TypeVar("TSharedNfoTagsValidator", bound=SharedNfoTagsValidator) + class SharedNfoTagsOptions( - PluginOptions, Generic[TStringFormatterValidator, TDictFormatterValidator], ABC + PluginOptions, + Generic[TStringFormatterValidator, TDictFormatterValidator, TSharedNfoTagsValidator], + ABC, ): """ Shared code between NFO tags and Ouptut Directory NFO Tags """ _formatter_validator: Type[TStringFormatterValidator] - _tags_validator: Type[ - SharedNfoTagsValidator[TStringFormatterValidator, TDictFormatterValidator] - ] + _tags_validator: Type[TSharedNfoTagsValidator] _required_keys = {"nfo_name", "nfo_root", "tags"} _optional_keys = {"kodi_safe"} @@ -69,7 +72,7 @@ class SharedNfoTagsOptions( return self._nfo_root @property - def tags(self) -> SharedNfoTagsValidator[TStringFormatterValidator, TDictFormatterValidator]: + def tags(self) -> TSharedNfoTagsValidator: """ Tags within the nfo_root tag. In the usage above, it would look like @@ -81,6 +84,22 @@ class SharedNfoTagsOptions( 2022 502 + + Also supports xml attributes: + + .. code-block:: yaml + + tags: + season: + attributes: + name: "Best Year" + tag: "{upload_year}" + + Which translates to + + .. code-block:: xml + + 2022 """ return self._tags @@ -95,8 +114,12 @@ class SharedNfoTagsOptions( class SharedNfoTagsPlugin( - Plugin[SharedNfoTagsOptions[TStringFormatterValidator, TDictFormatterValidator]], - Generic[TStringFormatterValidator, TDictFormatterValidator], + Plugin[ + SharedNfoTagsOptions[ + TStringFormatterValidator, TDictFormatterValidator, TSharedNfoTagsValidator + ] + ], + Generic[TStringFormatterValidator, TDictFormatterValidator, TSharedNfoTagsValidator], ABC, ): """ @@ -162,7 +185,9 @@ class SharedNfoTagsPlugin( self.save_file(file_name=nfo_file_name, file_metadata=nfo_metadata, entry=entry) -class NfoTagsOptions(SharedNfoTagsOptions[StringFormatterValidator, DictFormatterValidator]): +class NfoTagsOptions( + SharedNfoTagsOptions[StringFormatterValidator, DictFormatterValidator, NfoTagsValidator] +): """ Adds an NFO file for every download file. An NFO file is simply an XML file with a ``.nfo`` extension. You can add any values into the NFO. @@ -190,7 +215,9 @@ class NfoTagsOptions(SharedNfoTagsOptions[StringFormatterValidator, DictFormatte _tags_validator = NfoTagsValidator -class NfoTagsPlugin(SharedNfoTagsPlugin[StringFormatterValidator, DictFormatterValidator]): +class NfoTagsPlugin( + SharedNfoTagsPlugin[StringFormatterValidator, DictFormatterValidator, NfoTagsValidator] +): plugin_options_type = NfoTagsOptions def post_process_entry(self, entry: Entry) -> None: diff --git a/src/ytdl_sub/plugins/output_directory_nfo_tags.py b/src/ytdl_sub/plugins/output_directory_nfo_tags.py index 31c07da4..85dbd20c 100644 --- a/src/ytdl_sub/plugins/output_directory_nfo_tags.py +++ b/src/ytdl_sub/plugins/output_directory_nfo_tags.py @@ -9,11 +9,13 @@ from ytdl_sub.validators.string_formatter_validators import OverridesStringForma class OutputDirectoryNfoTagsOptions( - SharedNfoTagsOptions[OverridesStringFormatterValidator, OverridesDictFormatterValidator] + SharedNfoTagsOptions[ + OverridesStringFormatterValidator, OverridesDictFormatterValidator, NfoOverrideTagsValidator + ] ): """ - Adds a single NFO file in the output directory. An NFO file is simply an XML file with a - ``.nfo`` extension. You can add any values into the NFO. + Adds a single NFO file in the output directory. An NFO file is simply an XML file with a + ``.nfo`` extension. You can add any strings or override variables into this NFO. Usage: @@ -68,6 +70,22 @@ class OutputDirectoryNfoTagsOptions( Sweet youtube TV show + + Also supports xml attributes: + + .. code-block:: yaml + + tags: + title: + attributes: + year: "2022" + tag: "Sweet youtube TV show" + + Which translates to + + .. code-block:: xml + + Sweet youtube TV show</season> """ return self._tags @@ -82,7 +100,11 @@ class OutputDirectoryNfoTagsOptions( class OutputDirectoryNfoTagsPlugin( - SharedNfoTagsPlugin[OverridesStringFormatterValidator, OverridesDictFormatterValidator] + SharedNfoTagsPlugin[ + OverridesStringFormatterValidator, + OverridesDictFormatterValidator, + OutputDirectoryNfoTagsOptions, + ] ): plugin_options_type = OutputDirectoryNfoTagsOptions diff --git a/tests/e2e/plugins/test_nfo_tags.py b/tests/e2e/plugins/test_nfo_tags.py index 7d0fb600..11363a09 100644 --- a/tests/e2e/plugins/test_nfo_tags.py +++ b/tests/e2e/plugins/test_nfo_tags.py @@ -2,6 +2,7 @@ import pytest from e2e.expected_transaction_log import assert_transaction_log_matches from ytdl_sub.subscriptions.subscription import Subscription +from ytdl_sub.utils.exceptions import ValidationException @pytest.fixture @@ -60,3 +61,16 @@ class TestNfoTagsPlugins: transaction_log=transaction_log, transaction_log_summary_file_name=f"plugins/nfo_tags/{transaction_log_file_name}", ) + + def test_source_variable_in_output_directory_nfo_tags_errors( + self, subscription_dict, music_video_config + ): + subscription_dict["output_directory_nfo_tags"]["tags"]["kodi_safe_title_with_attrs"][ + "attributes" + ]["tag"] = "{title}" + with pytest.raises(ValidationException): + Subscription.from_dict( + config=music_video_config, + preset_name="kodi_safe_xml", + preset_dict=subscription_dict, + )