cleaned up preset validation

This commit is contained in:
Jesse Bannon 2023-12-12 00:01:07 -08:00
parent 5e85d23217
commit aca7a461b0
3 changed files with 72 additions and 87 deletions

View file

@ -38,6 +38,7 @@ from ytdl_sub.validators.string_formatter_validators import DictFormatterValidat
from ytdl_sub.validators.string_formatter_validators import OverridesDictFormatterValidator from ytdl_sub.validators.string_formatter_validators import OverridesDictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
from ytdl_sub.validators.string_formatter_validators import validate_formatters
from ytdl_sub.validators.validators import DictValidator from ytdl_sub.validators.validators import DictValidator
from ytdl_sub.validators.validators import ListValidator from ytdl_sub.validators.validators import ListValidator
from ytdl_sub.validators.validators import StringListValidator from ytdl_sub.validators.validators import StringListValidator
@ -202,7 +203,7 @@ class Preset(_PresetShell):
script = copy.deepcopy(self.overrides.script).add( script = copy.deepcopy(self.overrides.script).add(
ScriptUtils.add_dummy_variables(self._source_variables) ScriptUtils.add_dummy_variables(self._source_variables)
) )
unresolved_variables = copy.deepcopy(self._added_variables) unresolved_variables = self._added_variables
added_variables: Set[str] = self.downloader_options.added_source_variables( added_variables: Set[str] = self.downloader_options.added_source_variables(
unresolved_variables unresolved_variables
@ -236,14 +237,14 @@ class Preset(_PresetShell):
_ = script.resolve(unresolvable=unresolved_variables, update=True) _ = script.resolve(unresolvable=unresolved_variables, update=True)
# Validate that any formatter in the plugin options can resolve # Validate that any formatter in the plugin options can resolve
self._validate_formatters( validate_formatters(
mock_script=script, script=script,
unresolved_variables=unresolved_variables, unresolved_variables=unresolved_variables,
validator=plugin_options, validator=plugin_options,
) )
self._validate_formatters( validate_formatters(
mock_script=script, script=script,
unresolved_variables=unresolved_variables, unresolved_variables=unresolved_variables,
validator=self.output_options, validator=self.output_options,
) )
@ -252,66 +253,6 @@ class Preset(_PresetShell):
return script return script
def _validate_string_formatter_validator(
self,
mock_script: Script,
unresolved_variables: Set[str],
formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator],
) -> None:
try:
unresolvable = unresolved_variables
if isinstance(formatter_validator, OverridesStringFormatterValidator):
unresolvable = unresolved_variables.union({VARIABLES.entry_metadata.variable_name})
mock_script.resolve_once(
{"tmp_var": formatter_validator.format_string},
unresolvable=unresolvable,
)
except VariableDoesNotExist as exc:
raise StringFormattingVariableNotFoundException(exc) from exc
def _validate_formatters(
self,
mock_script: Script,
unresolved_variables: Set[str],
validator: Validator,
) -> None:
"""
Ensure all OverridesStringFormatterValidator's only contain variables from the overrides
and resolve.
"""
if isinstance(validator, DictValidator):
# 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():
self._validate_formatters(
mock_script=mock_script,
unresolved_variables=unresolved_variables,
validator=validator_value,
)
# pylint: enable=protected-access
elif isinstance(validator, ListValidator):
for list_value in validator.list:
self._validate_formatters(
mock_script=mock_script,
unresolved_variables=unresolved_variables,
validator=list_value,
)
elif isinstance(validator, (StringFormatterValidator, OverridesStringFormatterValidator)):
self._validate_string_formatter_validator(
mock_script=mock_script,
unresolved_variables=unresolved_variables,
formatter_validator=validator,
)
elif isinstance(validator, (DictFormatterValidator, OverridesDictFormatterValidator)):
for validator_value in validator.dict.values():
self._validate_string_formatter_validator(
mock_script=mock_script,
unresolved_variables=unresolved_variables,
formatter_validator=validator_value,
)
def _get_presets_to_merge( def _get_presets_to_merge(
self, parent_presets: str | List[str], seen_presets: List[str], config: ConfigValidator self, parent_presets: str | List[str], seen_presets: List[str], config: ConfigValidator
) -> List[Dict]: ) -> List[Dict]:
@ -349,7 +290,7 @@ class Preset(_PresetShell):
return presets_to_merge return presets_to_merge
def __merge_parent_preset_dicts_if_present(self, config: ConfigValidator): def _merge_parent_preset_dicts_if_present(self, config: ConfigValidator):
parent_preset_validator = self._validate_key_if_present( parent_preset_validator = self._validate_key_if_present(
key="preset", validator=StringListValidator key="preset", validator=StringListValidator
) )
@ -372,7 +313,7 @@ class Preset(_PresetShell):
super().__init__(name=name, value=value) super().__init__(name=name, value=value)
# Perform the merge of parent presets before validating any keys # Perform the merge of parent presets before validating any keys
self.__merge_parent_preset_dicts_if_present(config=config) self._merge_parent_preset_dicts_if_present(config=config)
self.downloader_options: MultiUrlValidator = self._validate_key( self.downloader_options: MultiUrlValidator = self._validate_key(
key="download", validator=MultiUrlValidator key="download", validator=MultiUrlValidator

View file

@ -3,6 +3,8 @@ from collections import OrderedDict
from keyword import iskeyword from keyword import iskeyword
from typing import Dict from typing import Dict
from typing import List from typing import List
from typing import Set
from typing import Union
from typing import final from typing import final
from yt_dlp.utils import sanitize_filename from yt_dlp.utils import sanitize_filename
@ -12,9 +14,11 @@ from ytdl_sub.script.parser import parse
from ytdl_sub.script.script import Script from ytdl_sub.script.script import Script
from ytdl_sub.script.types.resolvable import Resolvable from ytdl_sub.script.types.resolvable import Resolvable
from ytdl_sub.script.utils.exceptions import UserException from ytdl_sub.script.utils.exceptions import UserException
from ytdl_sub.script.utils.exceptions import VariableDoesNotExist
from ytdl_sub.utils.exceptions import InvalidVariableNameException from ytdl_sub.utils.exceptions import InvalidVariableNameException
from ytdl_sub.utils.exceptions import StringFormattingException from ytdl_sub.utils.exceptions import StringFormattingException
from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException
from ytdl_sub.validators.validators import DictValidator
from ytdl_sub.validators.validators import ListValidator from ytdl_sub.validators.validators import ListValidator
from ytdl_sub.validators.validators import LiteralDictValidator from ytdl_sub.validators.validators import LiteralDictValidator
from ytdl_sub.validators.validators import StringValidator from ytdl_sub.validators.validators import StringValidator
@ -172,3 +176,63 @@ class OverridesDictFormatterValidator(DictFormatterValidator):
""" """
_key_validator = OverridesStringFormatterValidator _key_validator = OverridesStringFormatterValidator
def _validate_formatter(
mock_script: Script,
unresolved_variables: Set[str],
formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator],
) -> None:
try:
unresolvable = unresolved_variables
if isinstance(formatter_validator, OverridesStringFormatterValidator):
unresolvable = unresolved_variables.union({VARIABLES.entry_metadata.variable_name})
mock_script.resolve_once(
{"tmp_var": formatter_validator.format_string},
unresolvable=unresolvable,
)
except VariableDoesNotExist as exc:
raise StringFormattingVariableNotFoundException(exc) from exc
def validate_formatters(
script: Script,
unresolved_variables: Set[str],
validator: Validator,
) -> None:
"""
Ensure all OverridesStringFormatterValidator's only contain variables from the overrides
and resolve.
"""
if isinstance(validator, DictValidator):
# 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():
validate_formatters(
script=script,
unresolved_variables=unresolved_variables,
validator=validator_value,
)
# pylint: enable=protected-access
elif isinstance(validator, ListValidator):
for list_value in validator.list:
validate_formatters(
script=script,
unresolved_variables=unresolved_variables,
validator=list_value,
)
elif isinstance(validator, (StringFormatterValidator, OverridesStringFormatterValidator)):
_validate_formatter(
mock_script=script,
unresolved_variables=unresolved_variables,
formatter_validator=validator,
)
elif isinstance(validator, (DictFormatterValidator, OverridesDictFormatterValidator)):
for validator_value in validator.dict.values():
_validate_formatter(
mock_script=script,
unresolved_variables=unresolved_variables,
formatter_validator=validator_value,
)

View file

@ -207,23 +207,3 @@ class TestPreset:
"output_options": output_options, "output_options": output_options,
}, },
) )
def test_preset_with_multi_url__contains_all_empty_urls_errors(
self, config_file, output_options
):
with pytest.raises(
ValidationException,
match=re.escape(
"Validation error in test.download: Must contain at least one "
"url that is non-empty"
),
):
_ = Preset(
config=config_file,
name="test",
value={
"download": [{"url": "{url}"}, {"url": "{url2}"}],
"output_options": output_options,
"overrides": {"url": "", "url2": ""},
},
)