
* implement LiteralStringVar and format/retrieval mechanism * use create safe * add cached properties to ConcatVarOperation * fix caches * also include self * fix inconsistencies in typings * use default factory not default * add missing docstring * experiment with immutable var data * solve pydantic issues * add sorted function * missing docs * forgot ellipses * give up on frozen * dang it darglint * fix string serialization bugs and remove unused code * add returns statement * whitespace moment * add simple test for string concat * export ConcatVarOperation
1545 lines
42 KiB
Python
1545 lines
42 KiB
Python
import json
|
|
import typing
|
|
from typing import Dict, List, Set, Tuple, Union
|
|
|
|
import pytest
|
|
from pandas import DataFrame
|
|
|
|
from reflex.base import Base
|
|
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
|
|
from reflex.experimental.vars.base import (
|
|
ConcatVarOperation,
|
|
ImmutableVar,
|
|
LiteralStringVar,
|
|
)
|
|
from reflex.state import BaseState
|
|
from reflex.utils.imports import ImportVar
|
|
from reflex.vars import (
|
|
BaseVar,
|
|
ComputedVar,
|
|
ImmutableVarData,
|
|
Var,
|
|
VarData,
|
|
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(set([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)}}"
|
|
assert (
|
|
str(var.contains("1", "hello")) == f'{{{expected}.some(e=>e[`hello`]==="1")}}'
|
|
)
|
|
|
|
|
|
@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",
|
|
[
|
|
(BaseVar(_var_name="foo", _var_type=int)),
|
|
(BaseVar(_var_name="bar", _var_type=float)),
|
|
],
|
|
)
|
|
def test_var_replace_with_invalid_kwargs(var):
|
|
with pytest.raises(TypeError) as excinfo:
|
|
var._replace(_this_should_fail=True)
|
|
assert "Unexpected keyword arguments" in str(excinfo.value)
|
|
|
|
|
|
def test_computed_var_replace_with_invalid_kwargs():
|
|
@computed_var(initial_value=1)
|
|
def test_var(state) -> int:
|
|
return 1
|
|
|
|
with pytest.raises(TypeError) as excinfo:
|
|
test_var._replace(_random_kwarg=True)
|
|
assert "Unexpected keyword arguments" in str(excinfo.value)
|
|
|
|
|
|
@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",
|
|
[
|
|
"ParentState",
|
|
"ChildState",
|
|
"GrandChildState",
|
|
"StateWithAnyVar",
|
|
],
|
|
)
|
|
def test_computed_var_without_annotation_error(request, fixture):
|
|
"""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.
|
|
"""
|
|
with pytest.raises(TypeError) as err:
|
|
state = request.getfixturevalue(fixture)
|
|
state.var_without_annotation.foo
|
|
full_name = state.var_without_annotation._var_full_name
|
|
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",
|
|
[
|
|
"StateWithCorrectVarAnnotation",
|
|
"StateWithWrongVarAnnotation",
|
|
],
|
|
)
|
|
def test_computed_var_with_annotation_error(request, fixture):
|
|
"""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.
|
|
"""
|
|
with pytest.raises(AttributeError) as err:
|
|
state = request.getfixturevalue(fixture)
|
|
state.var_with_annotation.foo
|
|
full_name = state.var_with_annotation._var_full_name
|
|
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
|
|
|
|
|
|
def test_retrival():
|
|
var_without_data = ImmutableVar.create("test")
|
|
assert var_without_data is not None
|
|
|
|
original_var_data = VarData(
|
|
state="Test",
|
|
imports={"react": [ImportVar(tag="useRef")]},
|
|
hooks={"const state = useContext(StateContexts.state)": None},
|
|
)
|
|
|
|
var_with_data = var_without_data._replace(merge_var_data=original_var_data)
|
|
|
|
f_string = f"foo{var_with_data}bar"
|
|
|
|
assert REFLEX_VAR_OPENING_TAG in f_string
|
|
assert REFLEX_VAR_CLOSING_TAG in f_string
|
|
|
|
result_var_data = Var.create_safe(f_string)._var_data
|
|
result_immutable_var_data = ImmutableVar.create_safe(f_string)._var_data
|
|
assert result_var_data is not None and result_immutable_var_data is not None
|
|
assert (
|
|
result_var_data.state
|
|
== result_immutable_var_data.state
|
|
== original_var_data.state
|
|
)
|
|
assert (
|
|
result_var_data.imports
|
|
== (
|
|
result_immutable_var_data.imports
|
|
if isinstance(result_immutable_var_data.imports, dict)
|
|
else {
|
|
k: list(v)
|
|
for k, v in result_immutable_var_data.imports
|
|
if k in original_var_data.imports
|
|
}
|
|
)
|
|
== original_var_data.imports
|
|
)
|
|
assert (
|
|
list(result_var_data.hooks.keys())
|
|
== (
|
|
list(result_immutable_var_data.hooks.keys())
|
|
if isinstance(result_immutable_var_data.hooks, dict)
|
|
else list(result_immutable_var_data.hooks)
|
|
)
|
|
== list(original_var_data.hooks.keys())
|
|
)
|
|
|
|
|
|
def test_fstring_concat():
|
|
original_var_with_data = Var.create_safe(
|
|
"imagination", _var_data=VarData(state="fear")
|
|
)
|
|
|
|
immutable_var_with_data = ImmutableVar.create_safe(
|
|
"consequences",
|
|
_var_data=VarData(
|
|
imports={
|
|
"react": [ImportVar(tag="useRef")],
|
|
"utils": [ImportVar(tag="useEffect")],
|
|
}
|
|
),
|
|
)
|
|
|
|
f_string = f"foo{original_var_with_data}bar{immutable_var_with_data}baz"
|
|
|
|
string_concat = LiteralStringVar.create(
|
|
f_string,
|
|
_var_data=VarData(
|
|
hooks={"const state = useContext(StateContexts.state)": None}
|
|
),
|
|
)
|
|
|
|
assert str(string_concat) == '"foo"+imagination+"bar"+consequences+"baz"'
|
|
assert isinstance(string_concat, ConcatVarOperation)
|
|
assert string_concat._get_all_var_data() == ImmutableVarData(
|
|
state="fear",
|
|
imports={
|
|
"react": [ImportVar(tag="useRef")],
|
|
"utils": [ImportVar(tag="useEffect")],
|
|
},
|
|
hooks={"const state = useContext(StateContexts.state)": None},
|
|
)
|
|
|
|
|
|
@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 $<reflex.Var>{"state": "state", "interpolations": [], "imports": {"/utils/context": [{"tag": "StateContexts", "is_default": false, "alias": null, "install": true, "render": true, "transpile": false}], "react": [{"tag": "useContext", "is_default": false, "alias": null, "install": true, "render": true, "transpile": false}]}, "hooks": {"const state = useContext(StateContexts.state)": null}, "string_length": 13}</reflex.Var>{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=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 <string>' 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),
|
|
f"{ATestState.get_full_name()}.value",
|
|
),
|
|
(
|
|
Var.create(f"{ATestState.value} string", _var_is_string=True),
|
|
f"`${{{ATestState.get_full_name()}.value}} string`",
|
|
),
|
|
(Var.create(ATestState.dict_val), f"{ATestState.get_full_name()}.dict_val"),
|
|
],
|
|
)
|
|
def test_var_name_unwrapped(var, expected):
|
|
assert var._var_name_unwrapped == expected
|
|
|
|
|
|
def cv_fget(state: BaseState) -> int:
|
|
return 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"deps,expected",
|
|
[
|
|
(["a"], {"a"}),
|
|
(["b"], {"b"}),
|
|
([ComputedVar(fget=cv_fget)], {"cv_fget"}),
|
|
],
|
|
)
|
|
def test_computed_var_deps(deps: List[Union[str, Var]], expected: Set[str]):
|
|
@computed_var(
|
|
deps=deps,
|
|
cache=True,
|
|
)
|
|
def test_var(state) -> int:
|
|
return 1
|
|
|
|
assert test_var._static_deps == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"deps",
|
|
[
|
|
[""],
|
|
[1],
|
|
["", "abc"],
|
|
],
|
|
)
|
|
def test_invalid_computed_var_deps(deps: List):
|
|
with pytest.raises(TypeError):
|
|
|
|
@computed_var(
|
|
deps=deps,
|
|
cache=True,
|
|
)
|
|
def test_var(state) -> int:
|
|
return 1
|