import json import typing from typing import Dict, List, Set, Tuple import pytest from pandas import DataFrame from reflex.base import Base from reflex.state import BaseState from reflex.vars import ( BaseVar, Var, computed_var, ) test_vars = [ BaseVar(_var_name="prop1", _var_type=int), BaseVar(_var_name="key", _var_type=str), BaseVar(_var_name="value", _var_type=str)._var_set_state("state"), BaseVar(_var_name="local", _var_type=str, _var_is_local=True)._var_set_state( "state" ), BaseVar(_var_name="local2", _var_type=str, _var_is_local=True), ] class ATestState(BaseState): """Test state.""" value: str dict_val: Dict[str, List] = {} @pytest.fixture def TestObj(): class TestObj(Base): foo: int bar: str return TestObj @pytest.fixture def ParentState(TestObj): class ParentState(BaseState): foo: int bar: int @computed_var def var_without_annotation(self): return TestObj return ParentState @pytest.fixture def ChildState(ParentState, TestObj): class ChildState(ParentState): @computed_var def var_without_annotation(self): return TestObj return ChildState @pytest.fixture def GrandChildState(ChildState, TestObj): class GrandChildState(ChildState): @computed_var def var_without_annotation(self): return TestObj return GrandChildState @pytest.fixture def StateWithAnyVar(TestObj): class StateWithAnyVar(BaseState): @computed_var def var_without_annotation(self) -> typing.Any: return TestObj return StateWithAnyVar @pytest.fixture def StateWithCorrectVarAnnotation(): class StateWithCorrectVarAnnotation(BaseState): @computed_var def var_with_annotation(self) -> str: return "Correct annotation" return StateWithCorrectVarAnnotation @pytest.fixture def StateWithWrongVarAnnotation(TestObj): class StateWithWrongVarAnnotation(BaseState): @computed_var def var_with_annotation(self) -> str: return TestObj return StateWithWrongVarAnnotation @pytest.fixture def StateWithInitialComputedVar(): class StateWithInitialComputedVar(BaseState): @computed_var(initial_value="Initial value") def var_with_initial_value(self) -> str: return "Runtime value" return StateWithInitialComputedVar @pytest.fixture def ChildWithInitialComputedVar(StateWithInitialComputedVar): class ChildWithInitialComputedVar(StateWithInitialComputedVar): @computed_var(initial_value="Initial value") def var_with_initial_value_child(self) -> str: return "Runtime value" return ChildWithInitialComputedVar @pytest.fixture def StateWithRuntimeOnlyVar(): class StateWithRuntimeOnlyVar(BaseState): @computed_var(initial_value=None) def var_raises_at_runtime(self) -> str: raise ValueError("So nicht, mein Freund") return StateWithRuntimeOnlyVar @pytest.fixture def ChildWithRuntimeOnlyVar(StateWithRuntimeOnlyVar): class ChildWithRuntimeOnlyVar(StateWithRuntimeOnlyVar): @computed_var(initial_value="Initial value") def var_raises_at_runtime_child(self) -> str: raise ValueError("So nicht, mein Freund") return ChildWithRuntimeOnlyVar @pytest.mark.parametrize( "prop,expected", zip( test_vars, [ "prop1", "key", "state.value", "state.local", "local2", ], ), ) def test_full_name(prop, expected): """Test that the full name of a var is correct. Args: prop: The var to test. expected: The expected full name. """ assert prop._var_full_name == expected @pytest.mark.parametrize( "prop,expected", zip( test_vars, ["{prop1}", "{key}", "{state.value}", "state.local", "local2"], ), ) def test_str(prop, expected): """Test that the string representation of a var is correct. Args: prop: The var to test. expected: The expected string representation. """ assert str(prop) == expected @pytest.mark.parametrize( "prop,expected", [ (BaseVar(_var_name="p", _var_type=int), 0), (BaseVar(_var_name="p", _var_type=float), 0.0), (BaseVar(_var_name="p", _var_type=str), ""), (BaseVar(_var_name="p", _var_type=bool), False), (BaseVar(_var_name="p", _var_type=list), []), (BaseVar(_var_name="p", _var_type=dict), {}), (BaseVar(_var_name="p", _var_type=tuple), ()), (BaseVar(_var_name="p", _var_type=set), set()), ], ) def test_default_value(prop, expected): """Test that the default value of a var is correct. Args: prop: The var to test. expected: The expected default value. """ assert prop.get_default_value() == expected @pytest.mark.parametrize( "prop,expected", zip( test_vars, [ "set_prop1", "set_key", "state.set_value", "state.set_local", "set_local2", ], ), ) def test_get_setter(prop, expected): """Test that the name of the setter function of a var is correct. Args: prop: The var to test. expected: The expected name of the setter function. """ assert prop.get_setter_name() == expected @pytest.mark.parametrize( "value,expected", [ (None, None), (1, BaseVar(_var_name="1", _var_type=int, _var_is_local=True)), ("key", BaseVar(_var_name="key", _var_type=str, _var_is_local=True)), (3.14, BaseVar(_var_name="3.14", _var_type=float, _var_is_local=True)), ([1, 2, 3], BaseVar(_var_name="[1, 2, 3]", _var_type=list, _var_is_local=True)), ( {"a": 1, "b": 2}, BaseVar(_var_name='{"a": 1, "b": 2}', _var_type=dict, _var_is_local=True), ), ], ) def test_create(value, expected): """Test the var create function. Args: value: The value to create a var from. expected: The expected name of the setter function. """ prop = Var.create(value) if value is None: assert prop == expected else: assert prop.equals(expected) # type: ignore def test_create_type_error(): """Test the var create function when inputs type error.""" class ErrorType: pass value = ErrorType() with pytest.raises(TypeError): Var.create(value) def v(value) -> Var: val = ( Var.create(json.dumps(value), _var_is_string=True, _var_is_local=False) if isinstance(value, str) else Var.create(value, _var_is_local=False) ) assert val is not None return val def test_basic_operations(TestObj): """Test the var operations. Args: TestObj: The test object. """ assert str(v(1) == v(2)) == "{((1) === (2))}" assert str(v(1) != v(2)) == "{((1) !== (2))}" assert str(v(1) < v(2)) == "{((1) < (2))}" assert str(v(1) <= v(2)) == "{((1) <= (2))}" assert str(v(1) > v(2)) == "{((1) > (2))}" assert str(v(1) >= v(2)) == "{((1) >= (2))}" assert str(v(1) + v(2)) == "{((1) + (2))}" assert str(v(1) - v(2)) == "{((1) - (2))}" assert str(v(1) * v(2)) == "{((1) * (2))}" assert str(v(1) / v(2)) == "{((1) / (2))}" assert str(v(1) // v(2)) == "{Math.floor((1) / (2))}" assert str(v(1) % v(2)) == "{((1) % (2))}" assert str(v(1) ** v(2)) == "{Math.pow((1) , (2))}" assert str(v(1) & v(2)) == "{((1) && (2))}" assert str(v(1) | v(2)) == "{((1) || (2))}" assert str(v([1, 2, 3])[v(0)]) == "{[1, 2, 3].at(0)}" assert str(v({"a": 1, "b": 2})["a"]) == '{{"a": 1, "b": 2}["a"]}' assert str(v("foo") == v("bar")) == '{(("foo") === ("bar"))}' assert ( str( Var.create("foo", _var_is_local=False) == Var.create("bar", _var_is_local=False) ) == "{((foo) === (bar))}" ) assert ( str( BaseVar( _var_name="foo", _var_type=str, _var_is_string=True, _var_is_local=True ) == BaseVar( _var_name="bar", _var_type=str, _var_is_string=True, _var_is_local=True ) ) == "((`foo`) === (`bar`))" ) assert ( str( BaseVar( _var_name="foo", _var_type=TestObj, _var_is_string=True, _var_is_local=False, ) ._var_set_state("state") .bar == BaseVar( _var_name="bar", _var_type=str, _var_is_string=True, _var_is_local=True ) ) == "{((state.foo.bar) === (`bar`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=TestObj)._var_set_state("state").bar) == "{state.foo.bar}" ) assert str(abs(v(1))) == "{Math.abs(1)}" assert str(v([1, 2, 3]).length()) == "{[1, 2, 3].length}" assert str(v([1, 2]) + v([3, 4])) == "{spreadArraysOrObjects(([1, 2]) , ([3, 4]))}" # Tests for reverse operation assert str(v([1, 2, 3]).reverse()) == "{[...[1, 2, 3]].reverse()}" assert str(v(["1", "2", "3"]).reverse()) == '{[...["1", "2", "3"]].reverse()}' assert ( str(BaseVar(_var_name="foo", _var_type=list)._var_set_state("state").reverse()) == "{[...state.foo].reverse()}" ) assert ( str(BaseVar(_var_name="foo", _var_type=list).reverse()) == "{[...foo].reverse()}" ) assert str(BaseVar(_var_name="foo", _var_type=str)._type()) == "{typeof foo}" # type: ignore assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() == str) # type: ignore == "{((typeof foo) === (`string`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() == str) # type: ignore == "{((typeof foo) === (`string`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() == int) # type: ignore == "{((typeof foo) === (`number`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() == list) # type: ignore == "{((typeof foo) === (`Array`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() == float) # type: ignore == "{((typeof foo) === (`number`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() == tuple) # type: ignore == "{((typeof foo) === (`Array`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() == dict) # type: ignore == "{((typeof foo) === (`Object`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() != str) # type: ignore == "{((typeof foo) !== (`string`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() != int) # type: ignore == "{((typeof foo) !== (`number`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() != list) # type: ignore == "{((typeof foo) !== (`Array`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() != float) # type: ignore == "{((typeof foo) !== (`number`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() != tuple) # type: ignore == "{((typeof foo) !== (`Array`))}" ) assert ( str(BaseVar(_var_name="foo", _var_type=str)._type() != dict) # type: ignore == "{((typeof foo) !== (`Object`))}" ) @pytest.mark.parametrize( "var, expected", [ (v([1, 2, 3]), "[1, 2, 3]"), (v(["1", "2", "3"]), '["1", "2", "3"]'), (BaseVar(_var_name="foo", _var_type=list)._var_set_state("state"), "state.foo"), (BaseVar(_var_name="foo", _var_type=list), "foo"), (v((1, 2, 3)), "[1, 2, 3]"), (v(("1", "2", "3")), '["1", "2", "3"]'), ( BaseVar(_var_name="foo", _var_type=tuple)._var_set_state("state"), "state.foo", ), (BaseVar(_var_name="foo", _var_type=tuple), "foo"), ], ) def test_list_tuple_contains(var, expected): assert str(var.contains(1)) == f"{{{expected}.includes(1)}}" assert str(var.contains("1")) == f'{{{expected}.includes("1")}}' assert str(var.contains(v(1))) == f"{{{expected}.includes(1)}}" assert str(var.contains(v("1"))) == f'{{{expected}.includes("1")}}' other_state_var = BaseVar(_var_name="other", _var_type=str)._var_set_state("state") other_var = BaseVar(_var_name="other", _var_type=str) assert str(var.contains(other_state_var)) == f"{{{expected}.includes(state.other)}}" assert str(var.contains(other_var)) == f"{{{expected}.includes(other)}}" @pytest.mark.parametrize( "var, expected", [ (v("123"), json.dumps("123")), (BaseVar(_var_name="foo", _var_type=str)._var_set_state("state"), "state.foo"), (BaseVar(_var_name="foo", _var_type=str), "foo"), ], ) def test_str_contains(var, expected): assert str(var.contains("1")) == f'{{{expected}.includes("1")}}' assert str(var.contains(v("1"))) == f'{{{expected}.includes("1")}}' other_state_var = BaseVar(_var_name="other", _var_type=str)._var_set_state("state") other_var = BaseVar(_var_name="other", _var_type=str) assert str(var.contains(other_state_var)) == f"{{{expected}.includes(state.other)}}" assert str(var.contains(other_var)) == f"{{{expected}.includes(other)}}" @pytest.mark.parametrize( "var, expected", [ (v({"a": 1, "b": 2}), '{"a": 1, "b": 2}'), (BaseVar(_var_name="foo", _var_type=dict)._var_set_state("state"), "state.foo"), (BaseVar(_var_name="foo", _var_type=dict), "foo"), ], ) def test_dict_contains(var, expected): assert str(var.contains(1)) == f"{{{expected}.hasOwnProperty(1)}}" assert str(var.contains("1")) == f'{{{expected}.hasOwnProperty("1")}}' assert str(var.contains(v(1))) == f"{{{expected}.hasOwnProperty(1)}}" assert str(var.contains(v("1"))) == f'{{{expected}.hasOwnProperty("1")}}' other_state_var = BaseVar(_var_name="other", _var_type=str)._var_set_state("state") other_var = BaseVar(_var_name="other", _var_type=str) assert ( str(var.contains(other_state_var)) == f"{{{expected}.hasOwnProperty(state.other)}}" ) assert str(var.contains(other_var)) == f"{{{expected}.hasOwnProperty(other)}}" @pytest.mark.parametrize( "var", [ BaseVar(_var_name="list", _var_type=List[int]), BaseVar(_var_name="tuple", _var_type=Tuple[int, int]), BaseVar(_var_name="str", _var_type=str), ], ) def test_var_indexing_lists(var): """Test that we can index into str, list or tuple vars. Args: var : The str, list or tuple base var. """ # Test basic indexing. assert str(var[0]) == f"{{{var._var_name}.at(0)}}" assert str(var[1]) == f"{{{var._var_name}.at(1)}}" # Test negative indexing. assert str(var[-1]) == f"{{{var._var_name}.at(-1)}}" @pytest.mark.parametrize( "var, type_", [ (BaseVar(_var_name="list", _var_type=List[int]), [int, int]), (BaseVar(_var_name="tuple", _var_type=Tuple[int, str]), [int, str]), ], ) def test_var_indexing_types(var, type_): """Test that indexing returns valid types. Args: var : The list, typle base var. type_ : The type on indexed object. """ assert var[2]._var_type == type_[0] assert var[3]._var_type == type_[1] def test_var_indexing_str(): """Test that we can index into str vars.""" str_var = BaseVar(_var_name="str", _var_type=str) # Test that indexing gives a type of Var[str]. assert isinstance(str_var[0], Var) assert str_var[0]._var_type == str # Test basic indexing. assert str(str_var[0]) == "{str.at(0)}" assert str(str_var[1]) == "{str.at(1)}" # Test negative indexing. assert str(str_var[-1]) == "{str.at(-1)}" @pytest.mark.parametrize( "var, index", [ (BaseVar(_var_name="lst", _var_type=List[int]), [1, 2]), (BaseVar(_var_name="lst", _var_type=List[int]), {"name": "dict"}), (BaseVar(_var_name="lst", _var_type=List[int]), {"set"}), ( BaseVar(_var_name="lst", _var_type=List[int]), ( 1, 2, ), ), (BaseVar(_var_name="lst", _var_type=List[int]), 1.5), (BaseVar(_var_name="lst", _var_type=List[int]), "str"), ( BaseVar(_var_name="lst", _var_type=List[int]), BaseVar(_var_name="string_var", _var_type=str), ), ( BaseVar(_var_name="lst", _var_type=List[int]), BaseVar(_var_name="float_var", _var_type=float), ), ( BaseVar(_var_name="lst", _var_type=List[int]), BaseVar(_var_name="list_var", _var_type=List[int]), ), ( BaseVar(_var_name="lst", _var_type=List[int]), BaseVar(_var_name="set_var", _var_type=Set[str]), ), ( BaseVar(_var_name="lst", _var_type=List[int]), BaseVar(_var_name="dict_var", _var_type=Dict[str, str]), ), (BaseVar(_var_name="str", _var_type=str), [1, 2]), (BaseVar(_var_name="lst", _var_type=str), {"name": "dict"}), (BaseVar(_var_name="lst", _var_type=str), {"set"}), ( BaseVar(_var_name="lst", _var_type=str), BaseVar(_var_name="string_var", _var_type=str), ), ( BaseVar(_var_name="lst", _var_type=str), BaseVar(_var_name="float_var", _var_type=float), ), (BaseVar(_var_name="str", _var_type=Tuple[str]), [1, 2]), (BaseVar(_var_name="lst", _var_type=Tuple[str]), {"name": "dict"}), (BaseVar(_var_name="lst", _var_type=Tuple[str]), {"set"}), ( BaseVar(_var_name="lst", _var_type=Tuple[str]), BaseVar(_var_name="string_var", _var_type=str), ), ( BaseVar(_var_name="lst", _var_type=Tuple[str]), BaseVar(_var_name="float_var", _var_type=float), ), ], ) def test_var_unsupported_indexing_lists(var, index): """Test unsupported indexing throws a type error. Args: var: The base var. index: The base var index. """ with pytest.raises(TypeError): var[index] @pytest.mark.parametrize( "var", [ BaseVar(_var_name="lst", _var_type=List[int]), BaseVar(_var_name="tuple", _var_type=Tuple[int, int]), BaseVar(_var_name="str", _var_type=str), ], ) def test_var_list_slicing(var): """Test that we can slice into str, list or tuple vars. Args: var : The str, list or tuple base var. """ assert str(var[:1]) == f"{{{var._var_name}.slice(0, 1)}}" assert str(var[:1]) == f"{{{var._var_name}.slice(0, 1)}}" assert str(var[:]) == f"{{{var._var_name}.slice(0, undefined)}}" def test_dict_indexing(): """Test that we can index into dict vars.""" dct = BaseVar(_var_name="dct", _var_type=Dict[str, int]) # Check correct indexing. assert str(dct["a"]) == '{dct["a"]}' assert str(dct["asdf"]) == '{dct["asdf"]}' @pytest.mark.parametrize( "var, index", [ ( BaseVar(_var_name="dict", _var_type=Dict[str, str]), [1, 2], ), ( BaseVar(_var_name="dict", _var_type=Dict[str, str]), {"name": "dict"}, ), ( BaseVar(_var_name="dict", _var_type=Dict[str, str]), {"set"}, ), ( BaseVar(_var_name="dict", _var_type=Dict[str, str]), ( 1, 2, ), ), ( BaseVar(_var_name="lst", _var_type=Dict[str, str]), BaseVar(_var_name="list_var", _var_type=List[int]), ), ( BaseVar(_var_name="lst", _var_type=Dict[str, str]), BaseVar(_var_name="set_var", _var_type=Set[str]), ), ( BaseVar(_var_name="lst", _var_type=Dict[str, str]), BaseVar(_var_name="dict_var", _var_type=Dict[str, str]), ), ( BaseVar(_var_name="df", _var_type=DataFrame), [1, 2], ), ( BaseVar(_var_name="df", _var_type=DataFrame), {"name": "dict"}, ), ( BaseVar(_var_name="df", _var_type=DataFrame), {"set"}, ), ( BaseVar(_var_name="df", _var_type=DataFrame), ( 1, 2, ), ), ( BaseVar(_var_name="df", _var_type=DataFrame), BaseVar(_var_name="list_var", _var_type=List[int]), ), ( BaseVar(_var_name="df", _var_type=DataFrame), BaseVar(_var_name="set_var", _var_type=Set[str]), ), ( BaseVar(_var_name="df", _var_type=DataFrame), BaseVar(_var_name="dict_var", _var_type=Dict[str, str]), ), ], ) def test_var_unsupported_indexing_dicts(var, index): """Test unsupported indexing throws a type error. Args: var: The base var. index: The base var index. """ with pytest.raises(TypeError): var[index] @pytest.mark.parametrize( "fixture,full_name", [ ("ParentState", "parent_state.var_without_annotation"), ("ChildState", "parent_state__child_state.var_without_annotation"), ( "GrandChildState", "parent_state__child_state__grand_child_state.var_without_annotation", ), ("StateWithAnyVar", "state_with_any_var.var_without_annotation"), ], ) def test_computed_var_without_annotation_error(request, fixture, full_name): """Test that a type error is thrown when an attribute of a computed var is accessed without annotating the computed var. Args: request: Fixture Request. fixture: The state fixture. full_name: The full name of the state var. """ with pytest.raises(TypeError) as err: state = request.getfixturevalue(fixture) state.var_without_annotation.foo assert ( err.value.args[0] == f"You must provide an annotation for the state var `{full_name}`. Annotation cannot be `typing.Any`" ) @pytest.mark.parametrize( "fixture,full_name", [ ( "StateWithCorrectVarAnnotation", "state_with_correct_var_annotation.var_with_annotation", ), ( "StateWithWrongVarAnnotation", "state_with_wrong_var_annotation.var_with_annotation", ), ], ) def test_computed_var_with_annotation_error(request, fixture, full_name): """Test that an Attribute error is thrown when a non-existent attribute of an annotated computed var is accessed or when the wrong annotation is provided to a computed var. Args: request: Fixture Request. fixture: The state fixture. full_name: The full name of the state var. """ with pytest.raises(AttributeError) as err: state = request.getfixturevalue(fixture) state.var_with_annotation.foo assert ( err.value.args[0] == f"The State var `{full_name}` has no attribute 'foo' or may have been annotated wrongly." ) @pytest.mark.parametrize( "fixture,var_name,expected_initial,expected_runtime,raises_at_runtime", [ ( "StateWithInitialComputedVar", "var_with_initial_value", "Initial value", "Runtime value", False, ), ( "ChildWithInitialComputedVar", "var_with_initial_value_child", "Initial value", "Runtime value", False, ), ( "StateWithRuntimeOnlyVar", "var_raises_at_runtime", None, None, True, ), ( "ChildWithRuntimeOnlyVar", "var_raises_at_runtime_child", "Initial value", None, True, ), ], ) def test_state_with_initial_computed_var( request, fixture, var_name, expected_initial, expected_runtime, raises_at_runtime ): """Test that the initial and runtime values of a computed var are correct. Args: request: Fixture Request. fixture: The state fixture. var_name: The name of the computed var. expected_initial: The expected initial value of the computed var. expected_runtime: The expected runtime value of the computed var. raises_at_runtime: Whether the computed var is runtime only. """ state = request.getfixturevalue(fixture)() state_name = state.get_full_name() initial_dict = state.dict(initial=True)[state_name] assert initial_dict[var_name] == expected_initial if raises_at_runtime: with pytest.raises(ValueError): state.dict()[state_name][var_name] else: runtime_dict = state.dict()[state_name] assert runtime_dict[var_name] == expected_runtime @pytest.mark.parametrize( "out, expected", [ (f"{BaseVar(_var_name='var', _var_type=str)}", "${var}"), ( f"testing f-string with {BaseVar(_var_name='myvar', _var_type=int)._var_set_state('state')}", 'testing f-string with ${"state": "state", "interpolations": [], "imports": {"/utils/context": [{"tag": "StateContexts", "is_default": false, "alias": null, "install": true, "render": true}], "react": [{"tag": "useContext", "is_default": false, "alias": null, "install": true, "render": true}]}, "hooks": ["const state = useContext(StateContexts.state)"], "string_length": 13}{state.myvar}', ), ( f"testing local f-string {BaseVar(_var_name='x', _var_is_local=True, _var_type=str)}", "testing local f-string x", ), ], ) def test_fstrings(out, expected): assert out == expected @pytest.mark.parametrize( ("value", "expect_state"), [ ([1], ""), ({"a": 1}, ""), ([Var.create_safe(1)._var_set_state("foo")], "foo"), ({"a": Var.create_safe(1)._var_set_state("foo")}, "foo"), ], ) def test_extract_state_from_container(value, expect_state): """Test that _var_state is extracted from containers containing BaseVar. Args: value: The value to create a var from. expect_state: The expected state. """ assert Var.create_safe(value)._var_state == expect_state @pytest.mark.parametrize( "value", [ "var", "\nvar", ], ) def test_fstring_roundtrip(value): """Test that f-string roundtrip carries state. Args: value: The value to create a Var from. """ var = BaseVar.create_safe(value)._var_set_state("state") rt_var = Var.create_safe(f"{var}") assert var._var_state == rt_var._var_state assert var._var_full_name_needs_state_prefix assert not rt_var._var_full_name_needs_state_prefix assert rt_var._var_name == var._var_full_name @pytest.mark.parametrize( "var", [ BaseVar(_var_name="var", _var_type=int), BaseVar(_var_name="var", _var_type=float), BaseVar(_var_name="var", _var_type=str), BaseVar(_var_name="var", _var_type=bool), BaseVar(_var_name="var", _var_type=dict), BaseVar(_var_name="var", _var_type=tuple), BaseVar(_var_name="var", _var_type=set), BaseVar(_var_name="var", _var_type=None), ], ) def test_unsupported_types_for_reverse(var): """Test that unsupported types for reverse throw a type error. Args: var: The base var. """ with pytest.raises(TypeError) as err: var.reverse() assert err.value.args[0] == f"Cannot reverse non-list var var." @pytest.mark.parametrize( "var", [ BaseVar(_var_name="var", _var_type=int), BaseVar(_var_name="var", _var_type=float), BaseVar(_var_name="var", _var_type=bool), BaseVar(_var_name="var", _var_type=set), BaseVar(_var_name="var", _var_type=None), ], ) def test_unsupported_types_for_contains(var): """Test that unsupported types for contains throw a type error. Args: var: The base var. """ with pytest.raises(TypeError) as err: assert var.contains(1) assert ( err.value.args[0] == f"Var var of type {var._var_type} does not support contains check." ) @pytest.mark.parametrize( "other", [ BaseVar(_var_name="other", _var_type=int), BaseVar(_var_name="other", _var_type=float), BaseVar(_var_name="other", _var_type=bool), BaseVar(_var_name="other", _var_type=list), BaseVar(_var_name="other", _var_type=dict), BaseVar(_var_name="other", _var_type=tuple), BaseVar(_var_name="other", _var_type=set), ], ) def test_unsupported_types_for_string_contains(other): with pytest.raises(TypeError) as err: assert BaseVar(_var_name="var", _var_type=str).contains(other) assert ( err.value.args[0] == f"'in ' requires string as left operand, not {other._var_type}" ) def test_unsupported_default_contains(): with pytest.raises(TypeError) as err: assert 1 in BaseVar(_var_name="var", _var_type=str) assert ( err.value.args[0] == "'in' operator not supported for Var types, use Var.contains() instead." ) @pytest.mark.parametrize( "operand1_var,operand2_var,operators", [ ( Var.create(10), Var.create(5), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "&", ], ), ( Var.create(10.5), Var.create(5), ["+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="], ), ( Var.create(5), Var.create(True), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "&", ], ), ( Var.create(10.5), Var.create(5.5), ["+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="], ), ( Var.create(10.5), Var.create(True), ["+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="], ), (Var.create("10"), Var.create("5"), ["+", ">", "<", "<=", ">="]), (Var.create([10, 20]), Var.create([5, 6]), ["+", ">", "<", "<=", ">="]), (Var.create([10, 20]), Var.create(5), ["*"]), (Var.create([10, 20]), Var.create(True), ["*"]), ( Var.create(True), Var.create(True), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "&", ], ), ], ) def test_valid_var_operations(operand1_var: Var, operand2_var, operators: List[str]): """Test that operations do not raise a TypeError. Args: operand1_var: left operand. operand2_var: right operand. operators: list of supported operators. """ for operator in operators: operand1_var.operation(op=operator, other=operand2_var) operand1_var.operation(op=operator, other=operand2_var, flip=True) @pytest.mark.parametrize( "operand1_var,operand2_var,operators", [ ( Var.create(10), Var.create(5), [ "^", "<<", ">>", ], ), ( Var.create(10.5), Var.create(5), [ "|", "^", "<<", ">>", "&", ], ), ( Var.create(10.5), Var.create(True), [ "|", "^", "<<", ">>", "&", ], ), ( Var.create(10.5), Var.create(5.5), [ "|", "^", "<<", ">>", "&", ], ), ( Var.create("10"), Var.create("5"), [ "-", "/", "//", "*", "%", "**", "|", "^", "<<", ">>", "&", ], ), ( Var.create([10, 20]), Var.create([5, 6]), [ "-", "/", "//", "*", "%", "**", "|", "^", "<<", ">>", "&", ], ), ( Var.create([10, 20]), Var.create(5), [ "+", "-", "/", "//", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create([10, 20]), Var.create(True), [ "+", "-", "/", "//", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create([10, 20]), Var.create("5"), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create([10, 20]), Var.create({"key": "value"}), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create([10, 20]), Var.create(5.5), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create({"key": "value"}), Var.create({"another_key": "another_value"}), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create({"key": "value"}), Var.create(5), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create({"key": "value"}), Var.create(True), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create({"key": "value"}), Var.create(5.5), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ( Var.create({"key": "value"}), Var.create("5"), [ "+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">=", "|", "^", "<<", ">>", "&", ], ), ], ) def test_invalid_var_operations(operand1_var: Var, operand2_var, operators: List[str]): for operator in operators: with pytest.raises(TypeError): operand1_var.operation(op=operator, other=operand2_var) with pytest.raises(TypeError): operand1_var.operation(op=operator, other=operand2_var, flip=True) @pytest.mark.parametrize( "var, expected", [ (Var.create("string_value", _var_is_string=True), "`string_value`"), (Var.create(1), "1"), (Var.create([1, 2, 3]), "[1, 2, 3]"), (Var.create({"foo": "bar"}), '{"foo": "bar"}'), (Var.create(ATestState.value, _var_is_string=True), "a_test_state.value"), ( Var.create(f"{ATestState.value} string", _var_is_string=True), "`${a_test_state.value} string`", ), (Var.create(ATestState.dict_val), "a_test_state.dict_val"), ], ) def test_var_name_unwrapped(var, expected): assert var._var_name_unwrapped == expected