From 1e68a99cddb012019252cc589d85b89380550aa8 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Tue, 14 Nov 2023 19:09:04 -0800 Subject: [PATCH] json friendly array output --- src/ytdl_sub/script/parser.py | 3 +++ src/ytdl_sub/script/types/array.py | 7 +++---- src/ytdl_sub/script/types/map.py | 19 ++----------------- src/ytdl_sub/script/types/resolvable.py | 18 ++++++++++++++++++ tests/unit/script/test_script.py | 2 +- tests/unit/script/types/test_array.py | 4 ++-- tests/unit/script/types/test_bool.py | 5 +++-- tests/unit/script/types/test_function.py | 5 +++-- tests/unit/script/types/test_string.py | 11 ++++++----- 9 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/ytdl_sub/script/parser.py b/src/ytdl_sub/script/parser.py index 44036228..6614775e 100644 --- a/src/ytdl_sub/script/parser.py +++ b/src/ytdl_sub/script/parser.py @@ -51,6 +51,7 @@ STRINGS_ONLY_ARGS = InvalidSyntaxException( "Strings can only be used as arguments to functions, maps, or arrays" ) + def UNEXPECTED_CHAR_ARGUMENT(parser: ArgumentParser): return InvalidSyntaxException(f"Unexpected character when parsing {parser.value} arguments") @@ -76,9 +77,11 @@ def _is_variable_start(char: str) -> bool: def _is_numeric_start(char: str) -> bool: return char.isnumeric() or char == "-" + def _is_string_start(char: str) -> bool: return char in ["'", '"'] + def _is_breakable(char: str) -> bool: return char in ["}", ",", ")", "]"] or char.isspace() diff --git a/src/ytdl_sub/script/types/array.py b/src/ytdl_sub/script/types/array.py index 3f7e1343..418f8557 100644 --- a/src/ytdl_sub/script/types/array.py +++ b/src/ytdl_sub/script/types/array.py @@ -1,3 +1,4 @@ +import json from dataclasses import dataclass from typing import Dict from typing import List @@ -6,6 +7,7 @@ from typing import Set from ytdl_sub.script.types.resolvable import ArgumentType from ytdl_sub.script.types.resolvable import NonHashable from ytdl_sub.script.types.resolvable import Resolvable +from ytdl_sub.script.types.resolvable import ResolvableToJson from ytdl_sub.script.types.variable import FunctionArgument from ytdl_sub.script.types.variable import Variable from ytdl_sub.script.types.variable_dependency import VariableDependency @@ -15,9 +17,6 @@ from ytdl_sub.script.types.variable_dependency import VariableDependency class Array(NonHashable): value: List[Resolvable] - def __str__(self): - return f"[{', '.join([str(val.value) for val in self.value])}]" - @dataclass(frozen=True) class UnresolvedArray(Array, VariableDependency, ArgumentType): @@ -63,5 +62,5 @@ class UnresolvedArray(Array, VariableDependency, ArgumentType): @dataclass(frozen=True) -class ResolvedArray(Array, Resolvable): +class ResolvedArray(Array, ResolvableToJson): pass diff --git a/src/ytdl_sub/script/types/map.py b/src/ytdl_sub/script/types/map.py index 2920104b..55ffeb71 100644 --- a/src/ytdl_sub/script/types/map.py +++ b/src/ytdl_sub/script/types/map.py @@ -8,6 +8,7 @@ from ytdl_sub.script.types.resolvable import ArgumentType from ytdl_sub.script.types.resolvable import Hashable from ytdl_sub.script.types.resolvable import NonHashable from ytdl_sub.script.types.resolvable import Resolvable +from ytdl_sub.script.types.resolvable import ResolvableToJson from ytdl_sub.script.types.variable import FunctionArgument from ytdl_sub.script.types.variable import Variable from ytdl_sub.script.types.variable_dependency import VariableDependency @@ -18,22 +19,6 @@ from ytdl_sub.utils.exceptions import StringFormattingException class Map(NonHashable): value: Dict[Hashable, Resolvable] - def to_native(self) -> Dict: - output = {} - for key, value in self.value.items(): - native_key = key.value - if isinstance(value, Map): - native_value = value.to_native() - else: - native_value = value.value - - output[native_key] = native_value - - return output - - def __str__(self): - return json.dumps(self.to_native()) - @dataclass(frozen=True) class UnresolvedMap(Map, VariableDependency, ArgumentType): @@ -92,5 +77,5 @@ class UnresolvedMap(Map, VariableDependency, ArgumentType): @dataclass(frozen=True) -class ResolvedMap(Map, Resolvable): +class ResolvedMap(Map, ResolvableToJson): pass diff --git a/src/ytdl_sub/script/types/resolvable.py b/src/ytdl_sub/script/types/resolvable.py index a5e548cd..de204ff7 100644 --- a/src/ytdl_sub/script/types/resolvable.py +++ b/src/ytdl_sub/script/types/resolvable.py @@ -1,4 +1,6 @@ +import json from abc import ABC +from abc import abstractmethod from dataclasses import dataclass from typing import Any from typing import Generic @@ -40,6 +42,22 @@ class NonHashable(ABC): pass +class ResolvableToJson(Resolvable, ABC): + @classmethod + def _to_native(cls, to_convert: Resolvable) -> Any: + if isinstance(to_convert.value, list): + return [cls._to_native(val) for val in to_convert.value] + if isinstance(to_convert.value, dict): + return { + cls._to_native(key): cls._to_native(value) + for key, value in to_convert.value.items() + } + return to_convert.value + + def __str__(self): + return json.dumps(self._to_native(self)) + + @dataclass(frozen=True) class ResolvableT(Hashable, ABC, Generic[T]): value: T diff --git a/tests/unit/script/test_script.py b/tests/unit/script/test_script.py index 5821da26..c7b84a7b 100644 --- a/tests/unit/script/test_script.py +++ b/tests/unit/script/test_script.py @@ -14,7 +14,7 @@ class TestSyntaxTree: "bb": "b", "cc": "{%custom_func(aa, bb)}", } - ).resolve() == {"aa": String("a"), "bb": String("b"), "cc": String("return [a, b]")} + ).resolve() == {"aa": String("a"), "bb": String("b"), "cc": String('return ["a", "b"]')} def test_simple(self): assert Script({"a": "a", "b": "{b_}", "b_": "b"}).resolve() == { diff --git a/tests/unit/script/types/test_array.py b/tests/unit/script/types/test_array.py index f4b0e8b7..b3ea68c7 100644 --- a/tests/unit/script/types/test_array.py +++ b/tests/unit/script/types/test_array.py @@ -20,7 +20,7 @@ class TestArray: def test_return_as_str(self): assert Script({"array": "str: {['a', 3.14]}"}).resolve() == { - "array": String("str: [a, 3.14]") + "array": String('str: ["a", 3.14]') } def test_nested_array(self): @@ -111,4 +111,4 @@ class TestArray: "bb": "b", "cc": "{%custom_func(aa, bb)}", } - ).resolve() == {"aa": String("a"), "bb": String("b"), "cc": String("return [a, b]")} + ).resolve() == {"aa": String("a"), "bb": String("b"), "cc": String('return ["a", "b"]')} diff --git a/tests/unit/script/types/test_bool.py b/tests/unit/script/types/test_bool.py index d89c8278..40499be1 100644 --- a/tests/unit/script/types/test_bool.py +++ b/tests/unit/script/types/test_bool.py @@ -3,8 +3,9 @@ from typing import Tuple import pytest -from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR, STRINGS_ONLY_ARGS +from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR from ytdl_sub.script.parser import NUMERICS_ONLY_ARGS +from ytdl_sub.script.parser import STRINGS_ONLY_ARGS from ytdl_sub.script.parser import UNEXPECTED_CHAR_ARGUMENT from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT from ytdl_sub.script.parser import ArgumentParser @@ -17,4 +18,4 @@ from ytdl_sub.script.utils.exceptions import InvalidSyntaxException class TestBool: - pass \ No newline at end of file + pass diff --git a/tests/unit/script/types/test_function.py b/tests/unit/script/types/test_function.py index d1b71f9c..6e7ded5d 100644 --- a/tests/unit/script/types/test_function.py +++ b/tests/unit/script/types/test_function.py @@ -3,8 +3,9 @@ from typing import Tuple import pytest -from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR, STRINGS_ONLY_ARGS +from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR from ytdl_sub.script.parser import NUMERICS_ONLY_ARGS +from ytdl_sub.script.parser import STRINGS_ONLY_ARGS from ytdl_sub.script.parser import UNEXPECTED_CHAR_ARGUMENT from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT from ytdl_sub.script.parser import ArgumentParser @@ -17,4 +18,4 @@ from ytdl_sub.script.utils.exceptions import InvalidSyntaxException class TestFunction: - pass \ No newline at end of file + pass diff --git a/tests/unit/script/types/test_string.py b/tests/unit/script/types/test_string.py index 2c155b7e..c8dd9a8e 100644 --- a/tests/unit/script/types/test_string.py +++ b/tests/unit/script/types/test_string.py @@ -3,8 +3,9 @@ from typing import Tuple import pytest -from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR, STRINGS_ONLY_ARGS +from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR from ytdl_sub.script.parser import NUMERICS_ONLY_ARGS +from ytdl_sub.script.parser import STRINGS_ONLY_ARGS from ytdl_sub.script.parser import UNEXPECTED_CHAR_ARGUMENT from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT from ytdl_sub.script.parser import ArgumentParser @@ -21,10 +22,10 @@ class TestString: "string", [ "{'323'}", - "{ \"4253\" }", - "{\"hi\"}", - "{ \"asfsd\" }", - "{\"sdfasf\"}", + '{ "4253" }', + '{"hi"}', + '{ "asfsd" }', + '{"sdfasf"}', "{ '3fsdf' }", ], )