Add contains, reverse operations for Var (#1679)
This commit is contained in:
parent
6bfce48b0c
commit
2e1aea9713
@ -685,6 +685,76 @@ class Var(ABC):
|
||||
"""
|
||||
return self.operation("||", other, type_=bool, flip=True)
|
||||
|
||||
def __contains__(self, _: Any) -> Var:
|
||||
"""Override the 'in' operator to alert the user that it is not supported.
|
||||
|
||||
Raises:
|
||||
TypeError: the operation is not supported
|
||||
"""
|
||||
raise TypeError(
|
||||
"'in' operator not supported for Var types, use Var.contains() instead."
|
||||
)
|
||||
|
||||
def contains(self, other: Any) -> Var:
|
||||
"""Check if a var contains the object `other`.
|
||||
|
||||
Args:
|
||||
other: The object to check.
|
||||
|
||||
Raises:
|
||||
TypeError: If the var is not a valid type: dict, list, tuple or str.
|
||||
|
||||
Returns:
|
||||
A var representing the contain check.
|
||||
"""
|
||||
if self.type_ is None or not (
|
||||
types._issubclass(self.type_, Union[dict, list, tuple, str])
|
||||
):
|
||||
raise TypeError(
|
||||
f"Var {self.full_name} of type {self.type_} does not support contains check."
|
||||
)
|
||||
if isinstance(other, str):
|
||||
other = Var.create(json.dumps(other), is_string=True)
|
||||
elif not isinstance(other, Var):
|
||||
other = Var.create(other)
|
||||
if types._issubclass(self.type_, Dict):
|
||||
return BaseVar(
|
||||
name=f"{self.full_name}.has({other.full_name})",
|
||||
type_=bool,
|
||||
is_local=self.is_local,
|
||||
)
|
||||
else: # str, list, tuple
|
||||
# For strings, the left operand must be a string.
|
||||
if types._issubclass(self.type_, str) and not types._issubclass(
|
||||
other.type_, str
|
||||
):
|
||||
raise TypeError(
|
||||
f"'in <string>' requires string as left operand, not {other.type_}"
|
||||
)
|
||||
return BaseVar(
|
||||
name=f"{self.full_name}.includes({other.full_name})",
|
||||
type_=bool,
|
||||
is_local=self.is_local,
|
||||
)
|
||||
|
||||
def reverse(self) -> Var:
|
||||
"""Reverse a list var.
|
||||
|
||||
Raises:
|
||||
TypeError: If the var is not a list.
|
||||
|
||||
Returns:
|
||||
A var with the reversed list.
|
||||
"""
|
||||
if self.type_ is None or not types._issubclass(self.type_, list):
|
||||
raise TypeError(f"Cannot reverse non-list var {self.full_name}.")
|
||||
|
||||
return BaseVar(
|
||||
name=f"[...{self.full_name}].reverse()",
|
||||
type_=self.type_,
|
||||
is_local=self.is_local,
|
||||
)
|
||||
|
||||
def foreach(self, fn: Callable) -> Var:
|
||||
"""Return a list of components. after doing a foreach on this var.
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import json
|
||||
import typing
|
||||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
@ -239,7 +240,11 @@ def test_create_type_error():
|
||||
|
||||
|
||||
def v(value) -> Var:
|
||||
val = Var.create(value, is_local=False)
|
||||
val = (
|
||||
Var.create(json.dumps(value), is_string=True, is_local=False)
|
||||
if isinstance(value, str)
|
||||
else Var.create(value, is_local=False)
|
||||
)
|
||||
assert val is not None
|
||||
return val
|
||||
|
||||
@ -273,6 +278,75 @@ def test_basic_operations(TestObj):
|
||||
assert str(abs(v(1))) == "{Math.abs(1)}"
|
||||
assert str(v([1, 2, 3]).length()) == "{[1, 2, 3].length}"
|
||||
|
||||
# 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(name="foo", state="state", type_=list).reverse())
|
||||
== "{[...state.foo].reverse()}"
|
||||
)
|
||||
assert str(BaseVar(name="foo", type_=list).reverse()) == "{[...foo].reverse()}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"var, expected",
|
||||
[
|
||||
(v([1, 2, 3]), "[1, 2, 3]"),
|
||||
(v(["1", "2", "3"]), '["1", "2", "3"]'),
|
||||
(BaseVar(name="foo", state="state", type_=list), "state.foo"),
|
||||
(BaseVar(name="foo", type_=list), "foo"),
|
||||
(v((1, 2, 3)), "[1, 2, 3]"),
|
||||
(v(("1", "2", "3")), '["1", "2", "3"]'),
|
||||
(BaseVar(name="foo", state="state", type_=tuple), "state.foo"),
|
||||
(BaseVar(name="foo", 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(name="other", state="state", type_=str)
|
||||
other_var = BaseVar(name="other", 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(name="foo", state="state", type_=str), "state.foo"),
|
||||
(BaseVar(name="foo", 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(name="other", state="state", type_=str)
|
||||
other_var = BaseVar(name="other", 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(name="foo", state="state", type_=dict), "state.foo"),
|
||||
(BaseVar(name="foo", type_=dict), "foo"),
|
||||
],
|
||||
)
|
||||
def test_dict_contains(var, expected):
|
||||
assert str(var.contains(1)) == f"{{{expected}.has(1)}}"
|
||||
assert str(var.contains("1")) == f'{{{expected}.has("1")}}'
|
||||
assert str(var.contains(v(1))) == f"{{{expected}.has(1)}}"
|
||||
assert str(var.contains(v("1"))) == f'{{{expected}.has("1")}}'
|
||||
other_state_var = BaseVar(name="other", state="state", type_=str)
|
||||
other_var = BaseVar(name="other", type_=str)
|
||||
assert str(var.contains(other_state_var)) == f"{{{expected}.has(state.other)}}"
|
||||
assert str(var.contains(other_var)) == f"{{{expected}.has(other)}}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"var",
|
||||
@ -632,3 +706,81 @@ def test_get_local_storage_raise_error(key):
|
||||
)
|
||||
def test_fstrings(out, expected):
|
||||
assert out == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"var",
|
||||
[
|
||||
BaseVar(name="var", type_=int),
|
||||
BaseVar(name="var", type_=float),
|
||||
BaseVar(name="var", type_=str),
|
||||
BaseVar(name="var", type_=bool),
|
||||
BaseVar(name="var", type_=dict),
|
||||
BaseVar(name="var", type_=tuple),
|
||||
BaseVar(name="var", type_=set),
|
||||
BaseVar(name="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(name="var", type_=int),
|
||||
BaseVar(name="var", type_=float),
|
||||
BaseVar(name="var", type_=bool),
|
||||
BaseVar(name="var", type_=set),
|
||||
BaseVar(name="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.type_} does not support contains check."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"other",
|
||||
[
|
||||
BaseVar(name="other", type_=int),
|
||||
BaseVar(name="other", type_=float),
|
||||
BaseVar(name="other", type_=bool),
|
||||
BaseVar(name="other", type_=list),
|
||||
BaseVar(name="other", type_=dict),
|
||||
BaseVar(name="other", type_=tuple),
|
||||
BaseVar(name="other", type_=set),
|
||||
],
|
||||
)
|
||||
def test_unsupported_types_for_string_contains(other):
|
||||
with pytest.raises(TypeError) as err:
|
||||
assert BaseVar(name="var", type_=str).contains(other)
|
||||
assert (
|
||||
err.value.args[0]
|
||||
== f"'in <string>' requires string as left operand, not {other.type_}"
|
||||
)
|
||||
|
||||
|
||||
def test_unsupported_default_contains():
|
||||
with pytest.raises(TypeError) as err:
|
||||
assert 1 in BaseVar(name="var", type_=str)
|
||||
assert (
|
||||
err.value.args[0]
|
||||
== "'in' operator not supported for Var types, use Var.contains() instead."
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user