force expected type
This commit is contained in:
parent
081d76c4e4
commit
5cb3028404
13 changed files with 43 additions and 54 deletions
|
|
@ -3,6 +3,8 @@ from typing import Dict
|
|||
from typing import Iterable
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
from typing import Type
|
||||
from typing import TypeVar
|
||||
|
||||
from ytdl_sub.entries.entry import Entry
|
||||
from ytdl_sub.entries.script.variable_definitions import VARIABLES
|
||||
|
|
@ -23,6 +25,8 @@ from ytdl_sub.utils.scriptable import Scriptable
|
|||
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
|
||||
from ytdl_sub.validators.string_formatter_validators import UnstructuredDictFormatterValidator
|
||||
|
||||
ExpectedT = TypeVar("ExpectedT")
|
||||
|
||||
|
||||
class Overrides(UnstructuredDictFormatterValidator, Scriptable):
|
||||
"""
|
||||
|
|
@ -206,7 +210,8 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
|
|||
formatter: StringFormatterValidator,
|
||||
entry: Optional[Entry] = None,
|
||||
function_overrides: Optional[Dict[str, str]] = None,
|
||||
) -> Any:
|
||||
expected_type: Type[ExpectedT] = str,
|
||||
) -> ExpectedT:
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -216,6 +221,8 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
|
|||
Optional. Entry to add source variables to the formatter
|
||||
function_overrides
|
||||
Optional. Explicit values to override the overrides themselves and source variables
|
||||
expected_type
|
||||
The expected type that should return. Defaults to string.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -226,17 +233,15 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
|
|||
StringFormattingException
|
||||
If the formatter that is trying to be resolved cannot
|
||||
"""
|
||||
return formatter.post_process(
|
||||
out = formatter.post_process(
|
||||
self._apply_to_resolvable(
|
||||
formatter=formatter, entry=entry, function_overrides=function_overrides
|
||||
).native
|
||||
)
|
||||
|
||||
def evaluate_boolean(
|
||||
self, formatter: StringFormatterValidator, entry: Optional[Entry] = None
|
||||
) -> bool:
|
||||
"""
|
||||
Apply a formatter, and evaluate it to a boolean
|
||||
"""
|
||||
output = self.apply_formatter(formatter=formatter, entry=entry)
|
||||
return ScriptUtils.bool_formatter_output(output)
|
||||
if not isinstance(out, expected_type):
|
||||
raise StringFormattingException(
|
||||
f"Expected type {expected_type.__name__}, but received '{out.__class__.__name__}'"
|
||||
)
|
||||
|
||||
return out
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class Plugin(BasePlugin[OptionsValidatorT], Generic[OptionsValidatorT], ABC):
|
|||
Returns True if enabled, False if disabled.
|
||||
"""
|
||||
if isinstance(self.plugin_options, ToggleableOptionsDictValidator):
|
||||
return self.overrides.evaluate_boolean(self.plugin_options.enable)
|
||||
return self.overrides.apply_formatter(self.plugin_options.enable, expected_type=bool)
|
||||
return True
|
||||
|
||||
def ytdl_options_match_filters(self) -> Tuple[List[str], List[str]]:
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
|
|||
entries_to_iter: List[Optional[Entry]] = entries
|
||||
|
||||
indices = list(range(len(entries_to_iter)))
|
||||
if self.overrides.evaluate_boolean(validator.download_reverse):
|
||||
if self.overrides.apply_formatter(validator.download_reverse, expected_type=bool):
|
||||
indices = reversed(indices)
|
||||
|
||||
for idx in indices:
|
||||
|
|
@ -461,8 +461,8 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
|
|||
ytdl_option_overrides=validator.ytdl_options.to_native_dict(self.overrides)
|
||||
)
|
||||
|
||||
include_sibling_metadata = self.overrides.evaluate_boolean(
|
||||
validator.include_sibling_metadata
|
||||
include_sibling_metadata = self.overrides.apply_formatter(
|
||||
validator.include_sibling_metadata, expected_type=bool
|
||||
)
|
||||
|
||||
parents, orphan_entries = self._download_url_metadata(
|
||||
|
|
@ -487,11 +487,9 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
|
|||
# download the bottom-most urls first since they are top-priority
|
||||
for idx, url_validator in reversed(list(enumerate(self.collection.urls.list))):
|
||||
# URLs can be empty. If they are, then skip
|
||||
if not (urls := self.overrides.apply_formatter(url_validator.url)):
|
||||
if not (urls := self.overrides.apply_formatter(url_validator.url, expected_type=list)):
|
||||
continue
|
||||
|
||||
assert isinstance(urls, list)
|
||||
|
||||
for url in reversed(urls):
|
||||
assert isinstance(url, str)
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ class DateRangePlugin(Plugin[DateRangeOptions]):
|
|||
date_validator=self.plugin_options.after, overrides=self.overrides
|
||||
)
|
||||
after_filter = f"{date_type} >= {after_str}"
|
||||
if self.overrides.evaluate_boolean(self.plugin_options.breaks):
|
||||
if self.overrides.apply_formatter(self.plugin_options.breaks, expected_type=bool):
|
||||
breaking_match_filters.append(after_filter)
|
||||
else:
|
||||
match_filters.append(after_filter)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class EmbedThumbnailPlugin(Plugin[EmbedThumbnailOptions]):
|
|||
|
||||
@property
|
||||
def _embed_thumbnail(self) -> bool:
|
||||
return self.overrides.evaluate_boolean(self.plugin_options)
|
||||
return self.overrides.apply_formatter(self.plugin_options, expected_type=bool)
|
||||
|
||||
@classmethod
|
||||
def _embed_video_thumbnail(cls, entry: Entry) -> None:
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ class FilterExcludePlugin(Plugin[FilterExcludeOptions]):
|
|||
return entry
|
||||
|
||||
for formatter in self.plugin_options.list:
|
||||
should_exclude = self.overrides.evaluate_boolean(formatter=formatter, entry=entry)
|
||||
should_exclude = self.overrides.apply_formatter(
|
||||
formatter=formatter, entry=entry, expected_type=bool
|
||||
)
|
||||
|
||||
if should_exclude:
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ from ytdl_sub.config.validators.options import OptionsValidator
|
|||
from ytdl_sub.entries.entry import Entry
|
||||
from ytdl_sub.utils.exceptions import StringFormattingException
|
||||
from ytdl_sub.utils.logger import Logger
|
||||
from ytdl_sub.utils.script import ScriptUtils
|
||||
from ytdl_sub.validators.string_formatter_validators import ListFormatterValidator
|
||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||
|
||||
|
|
@ -61,8 +60,8 @@ class FilterIncludePlugin(Plugin[FilterIncludeOptions]):
|
|||
return entry
|
||||
|
||||
for formatter in self.plugin_options.list:
|
||||
should_exclude = ScriptUtils.bool_formatter_output(
|
||||
self.overrides.apply_formatter(formatter=formatter, entry=entry)
|
||||
should_exclude = self.overrides.apply_formatter(
|
||||
formatter=formatter, entry=entry, expected_type=bool
|
||||
)
|
||||
if not should_exclude:
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class SharedNfoTagsPlugin(Plugin[SharedNfoTagsOptions], ABC):
|
|||
if not nfo_tags:
|
||||
return
|
||||
|
||||
if self.overrides.evaluate_boolean(self.plugin_options.kodi_safe):
|
||||
if self.overrides.apply_formatter(self.plugin_options.kodi_safe, expected_type=bool):
|
||||
nfo_root = to_max_3_byte_utf8_string(nfo_root)
|
||||
nfo_tags = {
|
||||
to_max_3_byte_utf8_string(key): [
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class SquareThumbnailPlugin(Plugin[SquareThumbnailOptions]):
|
|||
|
||||
@property
|
||||
def _square_thumbnail(self) -> bool:
|
||||
return self.overrides.evaluate_boolean(self.plugin_options)
|
||||
return self.overrides.apply_formatter(self.plugin_options, expected_type=bool)
|
||||
|
||||
@classmethod
|
||||
def _convert_to_square_thumbnail(cls, entry: Entry) -> None:
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ class _RandomizedRangeValidator(StrictDictValidator, ABC):
|
|||
)
|
||||
|
||||
def _randomized_float(self, overrides: Overrides, entry: Optional[Entry] = None) -> float:
|
||||
actualized_min = float(overrides.apply_formatter(self._min, entry=entry))
|
||||
actualized_max = float(overrides.apply_formatter(self._max, entry=entry))
|
||||
actualized_min = overrides.apply_formatter(self._min, entry=entry, expected_type=float)
|
||||
actualized_max = overrides.apply_formatter(self._max, entry=entry, expected_type=float)
|
||||
|
||||
if actualized_min < 0:
|
||||
raise self._validation_exception(
|
||||
|
|
@ -70,7 +70,7 @@ class _RandomizedRangeValidator(StrictDictValidator, ABC):
|
|||
-------
|
||||
Max possible value
|
||||
"""
|
||||
actualized_max = float(overrides.apply_formatter(self._max, entry=entry))
|
||||
actualized_max = overrides.apply_formatter(self._max, entry=entry, expected_type=float)
|
||||
if actualized_max < 0:
|
||||
raise self._validation_exception(
|
||||
f"max must be greater than zero, received {actualized_max}"
|
||||
|
|
|
|||
|
|
@ -94,15 +94,15 @@ class StringFormatterValidator(StringValidator):
|
|||
class FloatFormatterValidator(StringFormatterValidator):
|
||||
_expected_value_type_name = "float"
|
||||
|
||||
def post_process(self, resolved: str) -> str:
|
||||
def post_process(self, resolved: str) -> float:
|
||||
try:
|
||||
float(resolved)
|
||||
out = float(resolved)
|
||||
except Exception as exc:
|
||||
raise self._validation_exception(
|
||||
f"Expected a float, but received '{resolved}'"
|
||||
) from exc
|
||||
|
||||
return resolved
|
||||
return out
|
||||
|
||||
|
||||
class StandardizedDateValidator(StringFormatterValidator):
|
||||
|
|
@ -138,15 +138,14 @@ class OverridesStringFormatterValidator(StringFormatterValidator):
|
|||
class OverridesIntegerFormatterValidator(OverridesStringFormatterValidator):
|
||||
_expected_value_type_name = "integer"
|
||||
|
||||
def post_process(self, resolved: str) -> str:
|
||||
def post_process(self, resolved: str) -> int:
|
||||
try:
|
||||
int(resolved)
|
||||
out = int(resolved)
|
||||
except Exception as exc:
|
||||
raise self._validation_exception(
|
||||
f"Expected an integer, but received '{resolved}'"
|
||||
) from exc
|
||||
|
||||
return resolved
|
||||
return out
|
||||
|
||||
|
||||
class OverridesFloatFormatterValidator(FloatFormatterValidator, OverridesStringFormatterValidator):
|
||||
|
|
@ -158,6 +157,9 @@ class OverridesFloatFormatterValidator(FloatFormatterValidator, OverridesStringF
|
|||
class OverridesBooleanFormatterValidator(OverridesStringFormatterValidator):
|
||||
_expected_value_type_name = "boolean"
|
||||
|
||||
def post_process(self, resolved: Any) -> bool:
|
||||
return ScriptUtils.bool_formatter_output(output=str(resolved))
|
||||
|
||||
|
||||
class ListFormatterValidator(ListValidator[StringFormatterValidator]):
|
||||
_inner_list_type = StringFormatterValidator
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ class TestTvShowCollectionPreset:
|
|||
# mock so bilateral url gets enabled
|
||||
"subscription_has_download_archive": "True"
|
||||
},
|
||||
expected_type=list,
|
||||
)
|
||||
assert url == [
|
||||
f"youtube.com/playlist?url_{season_num}_{i}"
|
||||
|
|
@ -87,6 +88,7 @@ class TestTvShowCollectionPreset:
|
|||
# mock so bilateral url gets enabled
|
||||
"subscription_has_download_archive": "True"
|
||||
},
|
||||
expected_type=list,
|
||||
)
|
||||
|
||||
# First instance is the first url to get thumbnails
|
||||
|
|
|
|||
|
|
@ -42,25 +42,6 @@ class TestScriptUtils:
|
|||
output = single_variable_output(ScriptUtils.to_script(json_dict))
|
||||
assert output == expected_output
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_str, expected_output",
|
||||
[
|
||||
("", False),
|
||||
("true", True),
|
||||
("false", False),
|
||||
("[ ]", False),
|
||||
("{ }", False),
|
||||
("True", True),
|
||||
("False", False),
|
||||
("lol not False", True),
|
||||
("0", False),
|
||||
("-1", True),
|
||||
("1", True),
|
||||
],
|
||||
)
|
||||
def test_bool_formatter_output(self, input_str: str, expected_output: bool):
|
||||
assert ScriptUtils.bool_formatter_output(input_str) == expected_output
|
||||
|
||||
def test_to_syntax_tree(self):
|
||||
out = ScriptUtils.to_native_script(
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue