diff --git a/src/ytdl_sub/script/functions.py b/src/ytdl_sub/script/functions.py index 93842cd1..7c058a98 100644 --- a/src/ytdl_sub/script/functions.py +++ b/src/ytdl_sub/script/functions.py @@ -1,27 +1,67 @@ +from abc import ABC +from dataclasses import dataclass +from typing import Generic +from typing import TypeVar + +T = TypeVar("T") + + +@dataclass(frozen=True) +class Resolvable(ABC, Generic[T]): + value: T + + def resolve(self) -> str: + return str(self.value) + + +@dataclass(frozen=True) +class Integer(Resolvable[int]): + pass + + +@dataclass(frozen=True) +class Float(Resolvable[float]): + pass + + +@dataclass(frozen=True) +class Boolean(Resolvable[bool]): + pass + + +@dataclass(frozen=True) +class String(Resolvable[str]): + pass + + class Functions: @staticmethod - def lower(string: str) -> str: + def lower(string: String) -> String: """ Returns ------- Lower-cased string """ - return string.lower() + return String(string.value.lower()) @staticmethod - def upper(string: str) -> str: + def upper(string: String) -> String: """ Returns ------- Upper-cased string """ - return string.upper() + return String(string.value.upper()) @staticmethod - def capitalize(string: str) -> str: + def capitalize(string: String) -> String: """ Returns ------- Capitalized string """ - return string.capitalize() + return String(string.value.capitalize()) + + @staticmethod + def concat(l_string: String, r_string: String) -> String: + return String(f"{l_string}{r_string}") diff --git a/src/ytdl_sub/script/parser.py b/src/ytdl_sub/script/parser.py index daf75010..7d13005c 100644 --- a/src/ytdl_sub/script/parser.py +++ b/src/ytdl_sub/script/parser.py @@ -1,14 +1,14 @@ from typing import List from typing import Optional +from ytdl_sub.script.functions import Boolean +from ytdl_sub.script.functions import Float +from ytdl_sub.script.functions import Integer +from ytdl_sub.script.functions import String from ytdl_sub.script.types import ArgumentType -from ytdl_sub.script.types import Boolean -from ytdl_sub.script.types import Float from ytdl_sub.script.types import Function -from ytdl_sub.script.types import Integer from ytdl_sub.script.types import LiteralString from ytdl_sub.script.types import NumericType -from ytdl_sub.script.types import String from ytdl_sub.script.types import SyntaxTree from ytdl_sub.script.types import Variable from ytdl_sub.utils.exceptions import StringFormattingException diff --git a/src/ytdl_sub/script/types.py b/src/ytdl_sub/script/types.py index 1a0afc53..0e031c81 100644 --- a/src/ytdl_sub/script/types.py +++ b/src/ytdl_sub/script/types.py @@ -1,31 +1,18 @@ from dataclasses import dataclass -from typing import List, Dict, Optional +from typing import Dict +from typing import List +from typing import Optional from typing import Set from typing import Union +from ytdl_sub.script.functions import Boolean +from ytdl_sub.script.functions import Float +from ytdl_sub.script.functions import Functions +from ytdl_sub.script.functions import Integer +from ytdl_sub.script.functions import String from ytdl_sub.utils.exceptions import StringFormattingException -@dataclass(frozen=True) -class Integer: - value: int - - -@dataclass(frozen=True) -class Float: - value: float - - -@dataclass(frozen=True) -class Boolean: - value: bool - - -@dataclass(frozen=True) -class String: - value: str - - @dataclass(frozen=True) class Variable: name: str @@ -40,6 +27,12 @@ class Function: name: str args: List[ArgumentType] + def __post_init__(self): + try: + getattr(Functions, self.name) + except AttributeError: + raise StringFormattingException(f"Function name {self.name} does not exist") + @property def variables(self) -> Set[Variable]: """ @@ -68,6 +61,11 @@ class SyntaxTree: @property def variables(self) -> Set[Variable]: + """ + Returns + ------- + All variables used within the SyntaxTree + """ variables: Set[Variable] = set() for token in self.ast: if isinstance(token, Variable): @@ -79,11 +77,19 @@ class SyntaxTree: @classmethod def detect_cycles(cls, parsed_overrides: Dict[str, "SyntaxTree"]) -> None: + """ + Parameters + ---------- + parsed_overrides + ``overrides`` in a subscription, parsed into a SyntaxTree + """ variable_dependencies: Dict[Variable, Set[Variable]] = { Variable(name): ast.variables for name, ast in parsed_overrides.items() } - def _traverse(to_variable: Variable, visited_variables: Optional[List[Variable]] = None) -> None: + def _traverse( + to_variable: Variable, visited_variables: Optional[List[Variable]] = None + ) -> None: if visited_variables is None: visited_variables = [] @@ -97,4 +103,6 @@ class SyntaxTree: for variable in variable_dependencies.keys(): _traverse(variable) - + @classmethod + def resolve(cls, parsed_overrides: Dict[str, "SyntaxTree"]) -> Dict[str, str]: + raise NotImplemented() diff --git a/src/ytdl_sub/utils/thumbnail.py b/src/ytdl_sub/utils/thumbnail.py index 9d342f64..a999f01c 100644 --- a/src/ytdl_sub/utils/thumbnail.py +++ b/src/ytdl_sub/utils/thumbnail.py @@ -75,7 +75,7 @@ def download_and_convert_url_thumbnail( # timeout after 8 seconds with urlopen(thumbnail_url, timeout=1.0) as file: with tempfile.NamedTemporaryFile(delete=False) as thumbnail: - thumbnail.write(file._read()) + thumbnail.write(file.read()) try: os.makedirs(os.path.dirname(output_thumbnail_path), exist_ok=True) diff --git a/tests/unit/script/test_parser.py b/tests/unit/script/test_parser.py index 9775767e..26b6ec78 100644 --- a/tests/unit/script/test_parser.py +++ b/tests/unit/script/test_parser.py @@ -1,12 +1,12 @@ import pytest +from ytdl_sub.script.functions import Boolean +from ytdl_sub.script.functions import Float +from ytdl_sub.script.functions import Integer +from ytdl_sub.script.functions import String from ytdl_sub.script.parser import parse -from ytdl_sub.script.types import Boolean -from ytdl_sub.script.types import Float from ytdl_sub.script.types import Function -from ytdl_sub.script.types import Integer from ytdl_sub.script.types import LiteralString -from ytdl_sub.script.types import String from ytdl_sub.script.types import SyntaxTree from ytdl_sub.script.types import Variable from ytdl_sub.utils.exceptions import StringFormattingException