working?
This commit is contained in:
parent
c622cf68b5
commit
5482e9931c
6 changed files with 145 additions and 4 deletions
|
|
@ -17,11 +17,11 @@ from ytdl_sub.utils.exceptions import StringFormattingException
|
|||
from ytdl_sub.utils.exceptions import ValidationException
|
||||
from ytdl_sub.utils.script import ScriptUtils
|
||||
from ytdl_sub.utils.scriptable import Scriptable
|
||||
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
|
||||
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
|
||||
from ytdl_sub.validators.string_formatter_validators import UnstructuredDictFormatterValidator
|
||||
|
||||
|
||||
class Overrides(DictFormatterValidator, Scriptable):
|
||||
class Overrides(UnstructuredDictFormatterValidator, Scriptable):
|
||||
"""
|
||||
Allows you to define variables that can be used in any EntryFormatter or OverridesFormatter.
|
||||
|
||||
|
|
@ -51,11 +51,11 @@ class Overrides(DictFormatterValidator, Scriptable):
|
|||
|
||||
@classmethod
|
||||
def partial_validate(cls, name: str, value: Any) -> None:
|
||||
dict_formatter = DictFormatterValidator(name=name, value=value)
|
||||
dict_formatter = UnstructuredDictFormatterValidator(name=name, value=value)
|
||||
_ = [parse(format_string) for format_string in dict_formatter.dict_with_format_strings]
|
||||
|
||||
def __init__(self, name, value):
|
||||
DictFormatterValidator.__init__(self, name, value)
|
||||
UnstructuredDictFormatterValidator.__init__(self, name, value)
|
||||
Scriptable.__init__(self, initialize_base_script=True)
|
||||
|
||||
for key in self._keys:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,21 @@ import re
|
|||
from typing import Any
|
||||
from typing import Dict
|
||||
|
||||
from ytdl_sub.script.parser import parse
|
||||
from ytdl_sub.script.script import _is_function
|
||||
from ytdl_sub.script.types.array import Array
|
||||
from ytdl_sub.script.types.array import UnresolvedArray
|
||||
from ytdl_sub.script.types.function import BuiltInFunction
|
||||
from ytdl_sub.script.types.function import Function
|
||||
from ytdl_sub.script.types.map import UnresolvedMap
|
||||
from ytdl_sub.script.types.resolvable import AnyArgument
|
||||
from ytdl_sub.script.types.resolvable import Argument
|
||||
from ytdl_sub.script.types.resolvable import Boolean
|
||||
from ytdl_sub.script.types.resolvable import Float
|
||||
from ytdl_sub.script.types.resolvable import Integer
|
||||
from ytdl_sub.script.types.resolvable import String
|
||||
from ytdl_sub.script.types.syntax_tree import SyntaxTree
|
||||
from ytdl_sub.script.types.variable import Variable
|
||||
|
||||
|
||||
class ScriptUtils:
|
||||
|
|
@ -43,6 +57,64 @@ class ScriptUtils:
|
|||
|
||||
return out
|
||||
|
||||
@classmethod
|
||||
def _to_script_argument(cls, value: Any) -> Argument:
|
||||
# Handle simple types as above
|
||||
if value is None or (isinstance(value, str) and value == ""):
|
||||
return String("")
|
||||
if isinstance(value, str):
|
||||
ast = parse(text=value).ast
|
||||
if len(ast) == 1:
|
||||
return ast[0]
|
||||
return BuiltInFunction(name="concat", args=ast)
|
||||
if isinstance(value, int):
|
||||
return Integer(value)
|
||||
if isinstance(value, float):
|
||||
return Float(value)
|
||||
if isinstance(value, bool):
|
||||
return Boolean(value)
|
||||
if isinstance(value, list):
|
||||
return UnresolvedArray([cls._to_script_argument(val) for val in value])
|
||||
if isinstance(value, dict):
|
||||
return UnresolvedMap(
|
||||
{
|
||||
cls._to_script_argument(key): cls._to_script_argument(val)
|
||||
for key, val in value.items()
|
||||
}
|
||||
)
|
||||
|
||||
assert False, "should never reach here"
|
||||
|
||||
@classmethod
|
||||
def _to_script_code(cls, arg: Argument, top_level: bool = False) -> str:
|
||||
if not top_level and isinstance(arg, (Integer, Boolean, Float)):
|
||||
return str(arg.native)
|
||||
if isinstance(arg, String):
|
||||
if arg.native == "":
|
||||
return "" if top_level else "''"
|
||||
return arg.native if top_level else f"'''{arg.native}'''"
|
||||
elif isinstance(arg, Integer):
|
||||
out = f"%int({arg.native})"
|
||||
elif isinstance(arg, Boolean):
|
||||
out = f"%bool({arg.native})"
|
||||
elif isinstance(arg, Float):
|
||||
out = f"%float({arg.native})"
|
||||
elif isinstance(arg, UnresolvedArray):
|
||||
out = f"[ {', '.join(cls._to_script_code(val) for val in arg.value)} ]"
|
||||
elif isinstance(arg, UnresolvedMap):
|
||||
out = f"{{ {', '.join(f'{cls._to_script_code(key)}: {cls._to_script_code(val)}' for key, val in arg.value.items())} }}"
|
||||
elif isinstance(arg, Variable):
|
||||
out = arg.name
|
||||
elif isinstance(arg, Function):
|
||||
out = f"%{arg.name}( {', '.join(cls._to_script_code(val) for val in arg.args)} )"
|
||||
else:
|
||||
assert False, "ack"
|
||||
return f"{{ {out} }}" if top_level else out
|
||||
|
||||
@classmethod
|
||||
def to_native_script(cls, value: Any) -> str:
|
||||
return cls._to_script_code(cls._to_script_argument(value), top_level=True)
|
||||
|
||||
@classmethod
|
||||
def bool_formatter_output(cls, output: str) -> bool:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ 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.utils.exceptions import StringFormattingVariableNotFoundException
|
||||
from ytdl_sub.utils.script import ScriptUtils
|
||||
from ytdl_sub.validators.validators import DictValidator
|
||||
from ytdl_sub.validators.validators import ListValidator
|
||||
from ytdl_sub.validators.validators import LiteralDictValidator
|
||||
|
|
@ -148,6 +149,18 @@ class OverridesDictFormatterValidator(DictFormatterValidator):
|
|||
_key_validator = OverridesStringFormatterValidator
|
||||
|
||||
|
||||
class UnstructuredDictFormatterValidator(DictFormatterValidator):
|
||||
def __init__(self, name, value):
|
||||
# Convert the unstructured-ness into a script
|
||||
if isinstance(value, dict):
|
||||
value = {key: ScriptUtils.to_native_script(val) for key, val in value.items()}
|
||||
super().__init__(name, value)
|
||||
|
||||
|
||||
class UnstructuredOverridesDictFormatterValidator(UnstructuredDictFormatterValidator):
|
||||
_key_validator = OverridesStringFormatterValidator
|
||||
|
||||
|
||||
def to_variable_dependency_format_string(script: Script, parsed_format_string: SyntaxTree) -> str:
|
||||
"""
|
||||
Create a dummy format string that contains all variable deps as a string.
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ def single_video_preset_dict(output_directory):
|
|||
"overrides": {
|
||||
"music_video_artist": "JMC",
|
||||
"music_video_directory": output_directory,
|
||||
"test_override_map": {"{music_video_artist}": "{music_video_directory}"},
|
||||
"test_override_map_get": "{ %map_get(test_override_map, music_video_artist) }",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import copy
|
|||
import pytest
|
||||
from unit.script.conftest import single_variable_output
|
||||
|
||||
from ytdl_sub.script.parser import parse
|
||||
from ytdl_sub.utils.script import ScriptUtils
|
||||
|
||||
|
||||
|
|
@ -51,3 +52,10 @@ class TestScriptUtils:
|
|||
)
|
||||
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(
|
||||
{"{var_a}": "{var_b}", "static_a": "string with {var_c} in it"}
|
||||
)
|
||||
out2 = parse(out)
|
||||
assert False
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ 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 OverridesStringFormatterValidator
|
||||
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
|
||||
from ytdl_sub.validators.string_formatter_validators import UnstructuredDictFormatterValidator
|
||||
from ytdl_sub.validators.string_formatter_validators import (
|
||||
UnstructuredOverridesDictFormatterValidator,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -75,3 +79,45 @@ class TestDictFormatterValidator(object):
|
|||
"key1": key1_format_string,
|
||||
"key2": key2_format_string,
|
||||
}
|
||||
|
||||
|
||||
class TestUnstructuredDictFormatterValidator(object):
|
||||
@pytest.mark.parametrize(
|
||||
"dict_validator_class, expected_formatter_class",
|
||||
[
|
||||
(UnstructuredDictFormatterValidator, StringFormatterValidator),
|
||||
(UnstructuredOverridesDictFormatterValidator, OverridesStringFormatterValidator),
|
||||
],
|
||||
)
|
||||
def test_validates_values(self, dict_validator_class, expected_formatter_class):
|
||||
key1_format_string = "string with {variable}"
|
||||
key2_format_string = "no variables"
|
||||
key3_int = 3
|
||||
key4_float = 4.132
|
||||
key5_bool = True
|
||||
key6_map = {"{variable}_key": "value", "static_key": "{variable}_value"}
|
||||
key7_list = ["list_1", "list_{variable_2}"]
|
||||
validator = dict_validator_class(
|
||||
name="validator",
|
||||
value={
|
||||
"key1": key1_format_string,
|
||||
"key2": key2_format_string,
|
||||
"key3": key3_int,
|
||||
"key4": key4_float,
|
||||
"key5": key5_bool,
|
||||
"key6": key6_map,
|
||||
"key7": key7_list,
|
||||
},
|
||||
)
|
||||
|
||||
assert len(validator.dict) == 7
|
||||
assert all(isinstance(val, expected_formatter_class) for val in validator.dict.values())
|
||||
assert validator.dict_with_format_strings == {
|
||||
"key1": "string with {variable}",
|
||||
"key2": "no variables",
|
||||
"key3": "{%int(3)}",
|
||||
"key4": "{%float(4.132)}",
|
||||
"key5": "{%int(True)}",
|
||||
"key6": '{%from_json(\'\'\'{"static_key": "{variable}_value", "{variable}_key": "value"}\'\'\')}',
|
||||
"key7": "{%from_json('''[\"list_1\", \"list_{variable_2}\"]''')}",
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue