json friendly array output

This commit is contained in:
Jesse Bannon 2023-11-14 19:09:04 -08:00
parent d50c0c6559
commit 1e68a99cdd
9 changed files with 41 additions and 33 deletions

View file

@ -51,6 +51,7 @@ STRINGS_ONLY_ARGS = InvalidSyntaxException(
"Strings can only be used as arguments to functions, maps, or arrays" "Strings can only be used as arguments to functions, maps, or arrays"
) )
def UNEXPECTED_CHAR_ARGUMENT(parser: ArgumentParser): def UNEXPECTED_CHAR_ARGUMENT(parser: ArgumentParser):
return InvalidSyntaxException(f"Unexpected character when parsing {parser.value} arguments") 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: def _is_numeric_start(char: str) -> bool:
return char.isnumeric() or char == "-" return char.isnumeric() or char == "-"
def _is_string_start(char: str) -> bool: def _is_string_start(char: str) -> bool:
return char in ["'", '"'] return char in ["'", '"']
def _is_breakable(char: str) -> bool: def _is_breakable(char: str) -> bool:
return char in ["}", ",", ")", "]"] or char.isspace() return char in ["}", ",", ")", "]"] or char.isspace()

View file

@ -1,3 +1,4 @@
import json
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict from typing import Dict
from typing import List 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 ArgumentType
from ytdl_sub.script.types.resolvable import NonHashable from ytdl_sub.script.types.resolvable import NonHashable
from ytdl_sub.script.types.resolvable import Resolvable 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 FunctionArgument
from ytdl_sub.script.types.variable import Variable from ytdl_sub.script.types.variable import Variable
from ytdl_sub.script.types.variable_dependency import VariableDependency 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): class Array(NonHashable):
value: List[Resolvable] value: List[Resolvable]
def __str__(self):
return f"[{', '.join([str(val.value) for val in self.value])}]"
@dataclass(frozen=True) @dataclass(frozen=True)
class UnresolvedArray(Array, VariableDependency, ArgumentType): class UnresolvedArray(Array, VariableDependency, ArgumentType):
@ -63,5 +62,5 @@ class UnresolvedArray(Array, VariableDependency, ArgumentType):
@dataclass(frozen=True) @dataclass(frozen=True)
class ResolvedArray(Array, Resolvable): class ResolvedArray(Array, ResolvableToJson):
pass pass

View file

@ -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 Hashable
from ytdl_sub.script.types.resolvable import NonHashable from ytdl_sub.script.types.resolvable import NonHashable
from ytdl_sub.script.types.resolvable import Resolvable 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 FunctionArgument
from ytdl_sub.script.types.variable import Variable from ytdl_sub.script.types.variable import Variable
from ytdl_sub.script.types.variable_dependency import VariableDependency from ytdl_sub.script.types.variable_dependency import VariableDependency
@ -18,22 +19,6 @@ from ytdl_sub.utils.exceptions import StringFormattingException
class Map(NonHashable): class Map(NonHashable):
value: Dict[Hashable, Resolvable] 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) @dataclass(frozen=True)
class UnresolvedMap(Map, VariableDependency, ArgumentType): class UnresolvedMap(Map, VariableDependency, ArgumentType):
@ -92,5 +77,5 @@ class UnresolvedMap(Map, VariableDependency, ArgumentType):
@dataclass(frozen=True) @dataclass(frozen=True)
class ResolvedMap(Map, Resolvable): class ResolvedMap(Map, ResolvableToJson):
pass pass

View file

@ -1,4 +1,6 @@
import json
from abc import ABC from abc import ABC
from abc import abstractmethod
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
from typing import Generic from typing import Generic
@ -40,6 +42,22 @@ class NonHashable(ABC):
pass 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) @dataclass(frozen=True)
class ResolvableT(Hashable, ABC, Generic[T]): class ResolvableT(Hashable, ABC, Generic[T]):
value: T value: T

View file

@ -14,7 +14,7 @@ class TestSyntaxTree:
"bb": "b", "bb": "b",
"cc": "{%custom_func(aa, bb)}", "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): def test_simple(self):
assert Script({"a": "a", "b": "{b_}", "b_": "b"}).resolve() == { assert Script({"a": "a", "b": "{b_}", "b_": "b"}).resolve() == {

View file

@ -20,7 +20,7 @@ class TestArray:
def test_return_as_str(self): def test_return_as_str(self):
assert Script({"array": "str: {['a', 3.14]}"}).resolve() == { 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): def test_nested_array(self):
@ -111,4 +111,4 @@ class TestArray:
"bb": "b", "bb": "b",
"cc": "{%custom_func(aa, bb)}", "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"]')}

View file

@ -3,8 +3,9 @@ from typing import Tuple
import pytest 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 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_CHAR_ARGUMENT
from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT
from ytdl_sub.script.parser import ArgumentParser from ytdl_sub.script.parser import ArgumentParser
@ -17,4 +18,4 @@ from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
class TestBool: class TestBool:
pass pass

View file

@ -3,8 +3,9 @@ from typing import Tuple
import pytest 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 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_CHAR_ARGUMENT
from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT
from ytdl_sub.script.parser import ArgumentParser from ytdl_sub.script.parser import ArgumentParser
@ -17,4 +18,4 @@ from ytdl_sub.script.utils.exceptions import InvalidSyntaxException
class TestFunction: class TestFunction:
pass pass

View file

@ -3,8 +3,9 @@ from typing import Tuple
import pytest 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 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_CHAR_ARGUMENT
from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT
from ytdl_sub.script.parser import ArgumentParser from ytdl_sub.script.parser import ArgumentParser
@ -21,10 +22,10 @@ class TestString:
"string", "string",
[ [
"{'323'}", "{'323'}",
"{ \"4253\" }", '{ "4253" }',
"{\"hi\"}", '{"hi"}',
"{ \"asfsd\" }", '{ "asfsd" }',
"{\"sdfasf\"}", '{"sdfasf"}',
"{ '3fsdf' }", "{ '3fsdf' }",
], ],
) )