diff --git a/src/ytdl_sub/script/functions/boolean_functions.py b/src/ytdl_sub/script/functions/boolean_functions.py index 52c23ad7..db038cf6 100644 --- a/src/ytdl_sub/script/functions/boolean_functions.py +++ b/src/ytdl_sub/script/functions/boolean_functions.py @@ -5,10 +5,16 @@ from ytdl_sub.script.types.resolvable import Boolean class BooleanFunctions: @staticmethod def bool(value: AnyType) -> Boolean: + """ + Cast any type to a boolean + """ return Boolean(bool(value.value)) @staticmethod def equals(left: AnyType, right: AnyType) -> Boolean: + """ + Returns True if left equals right. False otherwise. + """ return Boolean(left.value == right.value) @staticmethod diff --git a/src/ytdl_sub/script/functions/numeric_functions.py b/src/ytdl_sub/script/functions/numeric_functions.py index 39c1622e..82163d15 100644 --- a/src/ytdl_sub/script/functions/numeric_functions.py +++ b/src/ytdl_sub/script/functions/numeric_functions.py @@ -1,7 +1,11 @@ +from typing import Union + from ytdl_sub.script.types.resolvable import AnyType +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 Numeric +from ytdl_sub.script.types.resolvable import String def _to_numeric(value: int | float) -> Numeric: @@ -12,11 +16,11 @@ def _to_numeric(value: int | float) -> Numeric: class NumericFunctions: @staticmethod - def float(value: AnyType) -> Float: + def float(value: Union[Float, Integer, Boolean, String]) -> Float: return Float(value=float(value.value)) @staticmethod - def int(value: AnyType) -> Integer: + def int(value: Union[Float, Integer, Boolean, String]) -> Integer: return Integer(value=int(value.value)) @staticmethod @@ -36,8 +40,8 @@ class NumericFunctions: return _to_numeric(left.value / right.value) @staticmethod - def mod(value: Integer, modulo: Integer) -> Integer: - return Integer(value=value.value % modulo.value) + def mod(value: Numeric, modulo: Numeric) -> Numeric: + return _to_numeric(value=value.value % modulo.value) @staticmethod def max(left: Numeric, right: Numeric) -> Numeric: diff --git a/src/ytdl_sub/script/utils/type_checking.py b/src/ytdl_sub/script/utils/type_checking.py index 4d98df57..6bbbdacd 100644 --- a/src/ytdl_sub/script/utils/type_checking.py +++ b/src/ytdl_sub/script/utils/type_checking.py @@ -12,6 +12,7 @@ from ytdl_sub.script.types.resolvable import Resolvable from ytdl_sub.script.types.variable import Variable from ytdl_sub.script.utils.exceptions import UNREACHABLE +# pylint: disable=missing-raises-doc def is_union(arg_type: Type) -> bool: """ diff --git a/tests/unit/script/types/test_array.py b/tests/unit/script/types/test_array.py index 36ead92a..5bc17734 100644 --- a/tests/unit/script/types/test_array.py +++ b/tests/unit/script/types/test_array.py @@ -24,18 +24,6 @@ class TestArray: "array": String('str: ["a", 3.14]') } - @pytest.mark.parametrize( - "array, expected_bool", - [ - ("{%bool([])}", False), - ("{%bool([False])}", True), - ], - ) - def test_return_as_bool(self, array: str, expected_bool: bool): - assert Script({"array_as_bool": array}).resolve() == { - "array_as_bool": Boolean(expected_bool) - } - def test_nested_array(self): assert Script( {"array": "{['level1', ['level2', ['level3', 'level3'], 'level2'], 'level1']}"} diff --git a/tests/unit/script/types/test_bool.py b/tests/unit/script/types/test_bool.py index 96d9378b..255a095e 100644 --- a/tests/unit/script/types/test_bool.py +++ b/tests/unit/script/types/test_bool.py @@ -37,3 +37,23 @@ class TestBool: "boolean": Boolean(expected_boolean), "as_string": String(str(expected_boolean)), } + + @pytest.mark.parametrize( + "to_cast, expected_bool", + [ + ("{%bool(False)}", False), + ("{%bool(True)}", True), + ("{%bool(0)}", False), + ("{%bool(1)}", True), + ("{%bool(0.0)}", False), + ("{%bool(0.1)}", True), + ("{%bool('')}", False), + ("{%bool('false')}", True), + ("{%bool([])}", False), + ("{%bool([False])}", True), + ("{%bool({})}", False), + ("{%bool({'key': 'value'})}", True), + ], + ) + def test_cast_as_bool(self, to_cast: str, expected_bool: bool): + assert Script({"as_bool": to_cast}).resolve() == {"as_bool": Boolean(expected_bool)} diff --git a/tests/unit/script/types/test_float.py b/tests/unit/script/types/test_float.py index 763d7453..b6401293 100644 --- a/tests/unit/script/types/test_float.py +++ b/tests/unit/script/types/test_float.py @@ -44,16 +44,6 @@ class TestFloat: "as_string": String(str(expected_float)), } - @pytest.mark.parametrize( - "float_, expected_bool", - [ - ("{%bool(0.0)}", False), - ("{%bool(0.1)}", True), - ], - ) - def test_return_as_bool(self, float_: str, expected_bool: bool): - assert Script({"as_bool": float_}).resolve() == {"as_bool": Boolean(expected_bool)} - @pytest.mark.parametrize( "float_", [ @@ -68,3 +58,17 @@ class TestFloat: def test_invalid_float(self, float_: str): with pytest.raises(InvalidSyntaxException, match=re.escape(str(NUMERICS_INVALID_CHAR))): Script({"float": float_}).resolve() + + @pytest.mark.parametrize( + "to_cast, expected_float", + [ + ("{%float(5)}", 5.0), + ("{%float(0.9)}", 0.9), + ("{%float(-3.00)}", -3.0), + ("{%float(True)}", 1.0), + ("{%float(False)}", 0.0), + ("{%float('142.43')}", 142.43), + ], + ) + def test_cast_as_float(self, to_cast: str, expected_float: float): + assert Script({"as_float": to_cast}).resolve() == {"as_float": Float(expected_float)} diff --git a/tests/unit/script/types/test_integer.py b/tests/unit/script/types/test_integer.py index e696c0eb..8e49d722 100644 --- a/tests/unit/script/types/test_integer.py +++ b/tests/unit/script/types/test_integer.py @@ -44,16 +44,6 @@ class TestInteger: "as_string": String(str(expected_integer)), } - @pytest.mark.parametrize( - "integer, expected_bool", - [ - ("{%bool(0)}", False), - ("{%bool(1)}", True), - ], - ) - def test_return_as_bool(self, integer: str, expected_bool: bool): - assert Script({"as_bool": integer}).resolve() == {"as_bool": Boolean(expected_bool)} - @pytest.mark.parametrize( "integer", [ @@ -68,3 +58,17 @@ class TestInteger: def test_invalid_integer(self, integer: str): with pytest.raises(InvalidSyntaxException, match=re.escape(str(NUMERICS_INVALID_CHAR))): Script({"integer": integer}).resolve() + + @pytest.mark.parametrize( + "to_cast, expected_int", + [ + ("{%int(5)}", 5), + ("{%int(0.9)}", 0), + ("{%int(-3.00)}", -3), + ("{%int(True)}", 1), + ("{%int(False)}", 0), + ("{%int('142')}", 142), + ], + ) + def test_cast_as_integer(self, to_cast: str, expected_int: int): + assert Script({"as_int": to_cast}).resolve() == {"as_int": Integer(expected_int)} diff --git a/tests/unit/script/types/test_map.py b/tests/unit/script/types/test_map.py index b0502561..51159760 100644 --- a/tests/unit/script/types/test_map.py +++ b/tests/unit/script/types/test_map.py @@ -28,16 +28,6 @@ class TestMap: "map": String('json: {"a": 3.14}') } - @pytest.mark.parametrize( - "map_, expected_bool", - [ - ("{%bool({})}", False), - ("{%bool({'key': 'value'})}", True), - ], - ) - def test_return_as_bool(self, map_: str, expected_bool: bool): - assert Script({"as_bool": map_}).resolve() == {"as_bool": Boolean(expected_bool)} - def test_nested_map(self): map_str = """{ { diff --git a/tests/unit/script/types/test_string.py b/tests/unit/script/types/test_string.py index 3b37fac9..e9564227 100644 --- a/tests/unit/script/types/test_string.py +++ b/tests/unit/script/types/test_string.py @@ -49,16 +49,6 @@ class TestString: def test_string(self, string: str, expected_string: str): assert Script({"string": string}).resolve() == {"string": String(expected_string)} - @pytest.mark.parametrize( - "string, expected_bool", - [ - ("{%bool('')}", False), - ("{%bool('false')}", True), - ], - ) - def test_return_as_bool(self, string: str, expected_bool: bool): - assert Script({"as_bool": string}).resolve() == {"as_bool": Boolean(expected_bool)} - @pytest.mark.parametrize( "string", [