瀏覽代碼

Type Validation for Var Operations and Enhanced Compatibility (#1674)

Elijah Ahianyo 1 年之前
父節點
當前提交
f2b0915aff
共有 5 個文件被更改,包括 1359 次插入28 次删除
  1. 701 0
      integration/test_var_operations.py
  2. 16 0
      reflex/.templates/web/utils/state.js
  3. 1 0
      reflex/compiler/compiler.py
  4. 229 22
      reflex/vars.py
  5. 412 6
      tests/test_var.py

+ 701 - 0
integration/test_var_operations.py

@@ -0,0 +1,701 @@
+"""Integration tests for var operations."""
+from typing import Generator
+
+import pytest
+from selenium.webdriver.common.by import By
+
+from reflex.testing import AppHarness
+
+# pyright: reportOptionalMemberAccess=false, reportGeneralTypeIssues=false, reportUnknownMemberType=false
+
+
+def VarOperations():
+    """App with var operations."""
+    import reflex as rx
+
+    class VarOperationState(rx.State):
+        int_var1: int = 10
+        int_var2: int = 5
+        int_var3: int = 7
+        float_var1: float = 10.5
+        float_var2: float = 5.5
+        list1: list = [1, 2]
+        list2: list = [3, 4]
+        str_var1: str = "first"
+        str_var2: str = "second"
+        dict1: dict = {1: 2}
+        dict2: dict = {3: 4}
+
+    app = rx.App(state=VarOperationState)
+
+    @app.add_page
+    def index():
+        return rx.vstack(
+            # INT INT
+            rx.text(
+                VarOperationState.int_var1 + VarOperationState.int_var2,
+                id="int_add_int",
+            ),
+            rx.text(
+                VarOperationState.int_var1 * VarOperationState.int_var2,
+                id="int_mult_int",
+            ),
+            rx.text(
+                VarOperationState.int_var1 - VarOperationState.int_var2,
+                id="int_sub_int",
+            ),
+            rx.text(
+                VarOperationState.int_var1**VarOperationState.int_var2,
+                id="int_exp_int",
+            ),
+            rx.text(
+                VarOperationState.int_var1 / VarOperationState.int_var2,
+                id="int_div_int",
+            ),
+            rx.text(
+                VarOperationState.int_var1 // VarOperationState.int_var3,
+                id="int_floor_int",
+            ),
+            rx.text(
+                VarOperationState.int_var1 % VarOperationState.int_var2,
+                id="int_mod_int",
+            ),
+            rx.text(
+                VarOperationState.int_var1 | VarOperationState.int_var2,
+                id="int_or_int",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 > VarOperationState.int_var2).to_string(),
+                id="int_gt_int",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 < VarOperationState.int_var2).to_string(),
+                id="int_lt_int",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 >= VarOperationState.int_var2).to_string(),
+                id="int_gte_int",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 <= VarOperationState.int_var2).to_string(),
+                id="int_lte_int",
+            ),
+            rx.text(
+                VarOperationState.int_var1 & VarOperationState.int_var2,
+                id="int_and_int",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 | VarOperationState.int_var2).to_string(),
+                id="int_or_int",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 == VarOperationState.int_var2).to_string(),
+                id="int_eq_int",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 != VarOperationState.int_var2).to_string(),
+                id="int_neq_int",
+            ),
+            # INT FLOAT OR FLOAT INT
+            rx.text(
+                VarOperationState.float_var1 + VarOperationState.int_var2,
+                id="float_add_int",
+            ),
+            rx.text(
+                VarOperationState.float_var1 * VarOperationState.int_var2,
+                id="float_mult_int",
+            ),
+            rx.text(
+                VarOperationState.float_var1 - VarOperationState.int_var2,
+                id="float_sub_int",
+            ),
+            rx.text(
+                VarOperationState.float_var1**VarOperationState.int_var2,
+                id="float_exp_int",
+            ),
+            rx.text(
+                VarOperationState.float_var1 / VarOperationState.int_var2,
+                id="float_div_int",
+            ),
+            rx.text(
+                VarOperationState.float_var1 // VarOperationState.int_var3,
+                id="float_floor_int",
+            ),
+            rx.text(
+                VarOperationState.float_var1 % VarOperationState.int_var2,
+                id="float_mod_int",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 > VarOperationState.int_var2).to_string(),
+                id="float_gt_int",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 < VarOperationState.int_var2).to_string(),
+                id="float_lt_int",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 >= VarOperationState.int_var2
+                ).to_string(),
+                id="float_gte_int",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 <= VarOperationState.int_var2
+                ).to_string(),
+                id="float_lte_int",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 == VarOperationState.int_var2
+                ).to_string(),
+                id="float_eq_int",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 != VarOperationState.int_var2
+                ).to_string(),
+                id="float_neq_int",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 & VarOperationState.int_var2).to_string(),
+                id="float_and_int",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 | VarOperationState.int_var2).to_string(),
+                id="float_or_int",
+            ),
+            # INT, DICT
+            rx.text(
+                (VarOperationState.int_var1 | VarOperationState.dict1).to_string(),
+                id="int_or_dict",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 & VarOperationState.dict1).to_string(),
+                id="int_and_dict",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 == VarOperationState.dict1).to_string(),
+                id="int_eq_dict",
+            ),
+            rx.text(
+                (VarOperationState.int_var1 != VarOperationState.dict1).to_string(),
+                id="int_neq_dict",
+            ),
+            # FLOAT FLOAT
+            rx.text(
+                VarOperationState.float_var1 + VarOperationState.float_var2,
+                id="float_add_float",
+            ),
+            rx.text(
+                VarOperationState.float_var1 * VarOperationState.float_var2,
+                id="float_mult_float",
+            ),
+            rx.text(
+                VarOperationState.float_var1 - VarOperationState.float_var2,
+                id="float_sub_float",
+            ),
+            rx.text(
+                VarOperationState.float_var1**VarOperationState.float_var2,
+                id="float_exp_float",
+            ),
+            rx.text(
+                VarOperationState.float_var1 / VarOperationState.float_var2,
+                id="float_div_float",
+            ),
+            rx.text(
+                VarOperationState.float_var1 // VarOperationState.float_var2,
+                id="float_floor_float",
+            ),
+            rx.text(
+                VarOperationState.float_var1 % VarOperationState.float_var2,
+                id="float_mod_float",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 > VarOperationState.float_var2
+                ).to_string(),
+                id="float_gt_float",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 < VarOperationState.float_var2
+                ).to_string(),
+                id="float_lt_float",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 >= VarOperationState.float_var2
+                ).to_string(),
+                id="float_gte_float",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 <= VarOperationState.float_var2
+                ).to_string(),
+                id="float_lte_float",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 == VarOperationState.float_var2
+                ).to_string(),
+                id="float_eq_float",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 != VarOperationState.float_var2
+                ).to_string(),
+                id="float_neq_float",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 & VarOperationState.float_var2
+                ).to_string(),
+                id="float_and_float",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 | VarOperationState.float_var2
+                ).to_string(),
+                id="float_or_float",
+            ),
+            # FLOAT STR
+            rx.text(
+                VarOperationState.float_var1 | VarOperationState.str_var1,
+                id="float_or_str",
+            ),
+            rx.text(
+                VarOperationState.float_var1 & VarOperationState.str_var1,
+                id="float_and_str",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 == VarOperationState.str_var1
+                ).to_string(),
+                id="float_eq_str",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 != VarOperationState.str_var1
+                ).to_string(),
+                id="float_neq_str",
+            ),
+            # FLOAT LIST
+            rx.text(
+                (VarOperationState.float_var1 | VarOperationState.list1).to_string(),
+                id="float_or_list",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 & VarOperationState.list1).to_string(),
+                id="float_and_list",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 == VarOperationState.list1).to_string(),
+                id="float_eq_list",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 != VarOperationState.list1).to_string(),
+                id="float_neq_list",
+            ),
+            # FLOAT DICT
+            rx.text(
+                (VarOperationState.float_var1 | VarOperationState.dict1).to_string(),
+                id="float_or_dict",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 & VarOperationState.dict1).to_string(),
+                id="float_and_dict",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 == VarOperationState.dict1).to_string(),
+                id="float_eq_dict",
+            ),
+            rx.text(
+                (VarOperationState.float_var1 != VarOperationState.dict1).to_string(),
+                id="float_neq_dict",
+            ),
+            # STR STR
+            rx.text(
+                VarOperationState.str_var1 + VarOperationState.str_var2,
+                id="str_add_str",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 > VarOperationState.str_var2).to_string(),
+                id="str_gt_str",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 < VarOperationState.str_var2).to_string(),
+                id="str_lt_str",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 >= VarOperationState.str_var2).to_string(),
+                id="str_gte_str",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 <= VarOperationState.str_var2).to_string(),
+                id="str_lte_str",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 == VarOperationState.float_var2
+                ).to_string(),
+                id="str_eq_str",
+            ),
+            rx.text(
+                (
+                    VarOperationState.float_var1 != VarOperationState.float_var2
+                ).to_string(),
+                id="str_neq_str",
+            ),
+            rx.text(
+                VarOperationState.str_var1.contains("fir").to_string(),
+                id="str_contains",
+            ),
+            rx.text(
+                VarOperationState.str_var1 | VarOperationState.str_var1, id="str_or_str"
+            ),
+            rx.text(
+                VarOperationState.str_var1 & VarOperationState.str_var2,
+                id="str_and_str",
+            ),
+            # STR, INT
+            rx.text(
+                VarOperationState.str_var1 * VarOperationState.int_var2,
+                id="str_mult_int",
+            ),
+            rx.text(
+                VarOperationState.str_var1 & VarOperationState.int_var2,
+                id="str_and_int",
+            ),
+            rx.text(
+                VarOperationState.str_var1 | VarOperationState.int_var2, id="str_or_int"
+            ),
+            rx.text(
+                (VarOperationState.str_var1 == VarOperationState.int_var1).to_string(),
+                id="str_eq_int",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 != VarOperationState.int_var1).to_string(),
+                id="str_neq_int",
+            ),
+            # STR, LIST
+            rx.text(
+                VarOperationState.str_var1 | VarOperationState.list1, id="str_or_list"
+            ),
+            rx.text(
+                (VarOperationState.str_var1 & VarOperationState.list1).to_string(),
+                id="str_and_list",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 == VarOperationState.list1).to_string(),
+                id="str_eq_list",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 != VarOperationState.list1).to_string(),
+                id="str_neq_list",
+            ),
+            # STR, DICT
+            rx.text(
+                VarOperationState.str_var1 | VarOperationState.dict1, id="str_or_dict"
+            ),
+            rx.text(
+                (VarOperationState.str_var1 & VarOperationState.dict1).to_string(),
+                id="str_and_dict",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 == VarOperationState.dict1).to_string(),
+                id="str_eq_dict",
+            ),
+            rx.text(
+                (VarOperationState.str_var1 != VarOperationState.dict1).to_string(),
+                id="str_neq_dict",
+            ),
+            # LIST, LIST
+            rx.text(
+                (VarOperationState.list1 + VarOperationState.list2).to_string(),
+                id="list_add_list",
+            ),
+            rx.text(
+                (VarOperationState.list1 & VarOperationState.list2).to_string(),
+                id="list_and_list",
+            ),
+            rx.text(
+                (VarOperationState.list1 | VarOperationState.list2).to_string(),
+                id="list_or_list",
+            ),
+            rx.text(
+                (VarOperationState.list1 > VarOperationState.list2).to_string(),
+                id="list_gt_list",
+            ),
+            rx.text(
+                (VarOperationState.list1 < VarOperationState.list2).to_string(),
+                id="list_lt_list",
+            ),
+            rx.text(
+                (VarOperationState.list1 >= VarOperationState.list2).to_string(),
+                id="list_gte_list",
+            ),
+            rx.text(
+                (VarOperationState.list1 <= VarOperationState.list2).to_string(),
+                id="list_lte_list",
+            ),
+            rx.text(
+                (VarOperationState.list1 == VarOperationState.list2).to_string(),
+                id="list_eq_list",
+            ),
+            rx.text(
+                (VarOperationState.list1 != VarOperationState.list2).to_string(),
+                id="list_neq_list",
+            ),
+            rx.text(
+                VarOperationState.list1.contains(1).to_string(), id="list_contains"
+            ),
+            rx.text(VarOperationState.list1.reverse().to_string(), id="list_reverse"),
+            # LIST, INT
+            rx.text(
+                (VarOperationState.list1 * VarOperationState.int_var2).to_string(),
+                id="list_mult_int",
+            ),
+            rx.text(
+                (VarOperationState.list1 | VarOperationState.int_var1).to_string(),
+                id="list_or_int",
+            ),
+            rx.text(
+                (VarOperationState.list1 & VarOperationState.int_var1).to_string(),
+                id="list_and_int",
+            ),
+            rx.text(
+                (VarOperationState.list1 == VarOperationState.int_var1).to_string(),
+                id="list_eq_int",
+            ),
+            rx.text(
+                (VarOperationState.list1 != VarOperationState.int_var1).to_string(),
+                id="list_neq_int",
+            ),
+            # LIST, DICT
+            rx.text(
+                (VarOperationState.list1 | VarOperationState.dict1).to_string(),
+                id="list_or_dict",
+            ),
+            rx.text(
+                (VarOperationState.list1 & VarOperationState.dict1).to_string(),
+                id="list_and_dict",
+            ),
+            rx.text(
+                (VarOperationState.list1 == VarOperationState.dict1).to_string(),
+                id="list_eq_dict",
+            ),
+            rx.text(
+                (VarOperationState.list1 != VarOperationState.dict1).to_string(),
+                id="list_neq_dict",
+            ),
+            # DICT, DICT
+            rx.text(
+                (VarOperationState.dict1 | VarOperationState.dict2).to_string(),
+                id="dict_or_dict",
+            ),
+            rx.text(
+                (VarOperationState.dict1 & VarOperationState.dict2).to_string(),
+                id="dict_and_dict",
+            ),
+            rx.text(
+                (VarOperationState.dict1 == VarOperationState.dict2).to_string(),
+                id="dict_eq_dict",
+            ),
+            rx.text(
+                (VarOperationState.dict1 != VarOperationState.dict2).to_string(),
+                id="dict_neq_dict",
+            ),
+            rx.text(
+                VarOperationState.dict1.contains(1).to_string(), id="dict_contains"
+            ),
+        )
+
+    app.compile()
+
+
+@pytest.fixture(scope="session")
+def var_operations(tmp_path_factory) -> Generator[AppHarness, None, None]:
+    """Start VarOperations app at tmp_path via AppHarness.
+
+    Args:
+        tmp_path_factory: pytest tmp_path_factory fixture
+
+    Yields:
+        running AppHarness instance
+    """
+    with AppHarness.create(
+        root=tmp_path_factory.mktemp("var_operations"),
+        app_source=VarOperations,  # type: ignore
+    ) as harness:
+        assert harness.app_instance is not None, "app is not running"
+        yield harness
+
+
+@pytest.fixture
+def driver(var_operations: AppHarness):
+    """Get an instance of the browser open to the var operations app.
+
+    Args:
+        var_operations: harness for VarOperations app
+
+    Yields:
+        WebDriver instance.
+    """
+    driver = var_operations.frontend()
+    try:
+        assert var_operations.poll_for_clients()
+        yield driver
+    finally:
+        driver.quit()
+
+
+def test_var_operations(driver, var_operations: AppHarness):
+    """Test that the var operations produce the right results.
+
+    Args:
+        driver: selenium WebDriver open to the app
+        var_operations: AppHarness for the var operations app
+    """
+    assert var_operations.app_instance is not None, "app is not running"
+    # INT INT
+    assert driver.find_element(By.ID, "int_add_int").text == "15"
+    assert driver.find_element(By.ID, "int_mult_int").text == "50"
+    assert driver.find_element(By.ID, "int_sub_int").text == "5"
+    assert driver.find_element(By.ID, "int_exp_int").text == "100000"
+    assert driver.find_element(By.ID, "int_div_int").text == "2"
+    assert driver.find_element(By.ID, "int_floor_int").text == "1"
+    assert driver.find_element(By.ID, "int_mod_int").text == "0"
+    assert driver.find_element(By.ID, "int_gt_int").text == "true"
+    assert driver.find_element(By.ID, "int_lt_int").text == "false"
+    assert driver.find_element(By.ID, "int_gte_int").text == "true"
+    assert driver.find_element(By.ID, "int_lte_int").text == "false"
+    assert driver.find_element(By.ID, "int_and_int").text == "5"
+    assert driver.find_element(By.ID, "int_or_int").text == "10"
+    assert driver.find_element(By.ID, "int_eq_int").text == "false"
+    assert driver.find_element(By.ID, "int_neq_int").text == "true"
+
+    # INT FLOAT OR FLOAT INT
+    assert driver.find_element(By.ID, "float_add_int").text == "15.5"
+    assert driver.find_element(By.ID, "float_mult_int").text == "52.5"
+    assert driver.find_element(By.ID, "float_sub_int").text == "5.5"
+    assert driver.find_element(By.ID, "float_exp_int").text == "127628.15625"
+    assert driver.find_element(By.ID, "float_div_int").text == "2.1"
+    assert driver.find_element(By.ID, "float_floor_int").text == "1"
+    assert driver.find_element(By.ID, "float_mod_int").text == "0.5"
+    assert driver.find_element(By.ID, "float_gt_int").text == "true"
+    assert driver.find_element(By.ID, "float_lt_int").text == "false"
+    assert driver.find_element(By.ID, "float_gte_int").text == "true"
+    assert driver.find_element(By.ID, "float_lte_int").text == "false"
+    assert driver.find_element(By.ID, "float_eq_int").text == "false"
+    assert driver.find_element(By.ID, "float_neq_int").text == "true"
+    assert driver.find_element(By.ID, "float_and_int").text == "5"
+    assert driver.find_element(By.ID, "float_or_int").text == "10.5"
+
+    # INT, DICT
+    assert driver.find_element(By.ID, "int_or_dict").text == "10"
+    assert driver.find_element(By.ID, "int_and_dict").text == '{"1":2}'
+    assert driver.find_element(By.ID, "int_eq_dict").text == "false"
+    assert driver.find_element(By.ID, "int_neq_dict").text == "true"
+
+    # FLOAT FLOAT
+    assert driver.find_element(By.ID, "float_add_float").text == "16"
+    assert driver.find_element(By.ID, "float_mult_float").text == "57.75"
+    assert driver.find_element(By.ID, "float_sub_float").text == "5"
+    assert driver.find_element(By.ID, "float_exp_float").text == "413562.49323606625"
+    assert driver.find_element(By.ID, "float_div_float").text == "1.9090909090909092"
+    assert driver.find_element(By.ID, "float_floor_float").text == "1"
+    assert driver.find_element(By.ID, "float_mod_float").text == "5"
+    assert driver.find_element(By.ID, "float_gt_float").text == "true"
+    assert driver.find_element(By.ID, "float_lt_float").text == "false"
+    assert driver.find_element(By.ID, "float_gte_float").text == "true"
+    assert driver.find_element(By.ID, "float_lte_float").text == "false"
+    assert driver.find_element(By.ID, "float_eq_float").text == "false"
+    assert driver.find_element(By.ID, "float_neq_float").text == "true"
+    assert driver.find_element(By.ID, "float_and_float").text == "5.5"
+    assert driver.find_element(By.ID, "float_or_float").text == "10.5"
+
+    # FLOAT STR
+    assert driver.find_element(By.ID, "float_or_str").text == "10.5"
+    assert driver.find_element(By.ID, "float_and_str").text == "first"
+    assert driver.find_element(By.ID, "float_eq_str").text == "false"
+    assert driver.find_element(By.ID, "float_neq_str").text == "true"
+
+    # FLOAT,LIST
+    assert driver.find_element(By.ID, "float_or_list").text == "10.5"
+    assert driver.find_element(By.ID, "float_and_list").text == "[1,2]"
+    assert driver.find_element(By.ID, "float_eq_list").text == "false"
+    assert driver.find_element(By.ID, "float_neq_list").text == "true"
+
+    # FLOAT, DICT
+    assert driver.find_element(By.ID, "float_or_dict").text == "10.5"
+    assert driver.find_element(By.ID, "float_and_dict").text == '{"1":2}'
+    assert driver.find_element(By.ID, "float_eq_dict").text == "false"
+    assert driver.find_element(By.ID, "float_neq_dict").text == "true"
+
+    # STR STR
+    assert driver.find_element(By.ID, "str_add_str").text == "firstsecond"
+    assert driver.find_element(By.ID, "str_gt_str").text == "false"
+    assert driver.find_element(By.ID, "str_lt_str").text == "true"
+    assert driver.find_element(By.ID, "str_gte_str").text == "false"
+    assert driver.find_element(By.ID, "str_lte_str").text == "true"
+    assert driver.find_element(By.ID, "str_eq_str").text == "false"
+    assert driver.find_element(By.ID, "str_neq_str").text == "true"
+    assert driver.find_element(By.ID, "str_and_str").text == "second"
+    assert driver.find_element(By.ID, "str_or_str").text == "first"
+    assert driver.find_element(By.ID, "str_contains").text == "true"
+
+    # STR INT
+    assert (
+        driver.find_element(By.ID, "str_mult_int").text == "firstfirstfirstfirstfirst"
+    )
+    assert driver.find_element(By.ID, "str_and_int").text == "5"
+    assert driver.find_element(By.ID, "str_or_int").text == "first"
+    assert driver.find_element(By.ID, "str_eq_int").text == "false"
+    assert driver.find_element(By.ID, "str_neq_int").text == "true"
+
+    # STR, LIST
+    assert driver.find_element(By.ID, "str_and_list").text == "[1,2]"
+    assert driver.find_element(By.ID, "str_or_list").text == "first"
+    assert driver.find_element(By.ID, "str_eq_list").text == "false"
+    assert driver.find_element(By.ID, "str_neq_list").text == "true"
+
+    # STR, DICT
+
+    assert driver.find_element(By.ID, "str_or_dict").text == "first"
+    assert driver.find_element(By.ID, "str_and_dict").text == '{"1":2}'
+    assert driver.find_element(By.ID, "str_eq_dict").text == "false"
+    assert driver.find_element(By.ID, "str_neq_dict").text == "true"
+
+    # LIST,LIST
+    assert driver.find_element(By.ID, "list_add_list").text == "[1,2,3,4]"
+    assert driver.find_element(By.ID, "list_gt_list").text == "false"
+    assert driver.find_element(By.ID, "list_lt_list").text == "true"
+    assert driver.find_element(By.ID, "list_gte_list").text == "false"
+    assert driver.find_element(By.ID, "list_lte_list").text == "true"
+    assert driver.find_element(By.ID, "list_eq_list").text == "false"
+    assert driver.find_element(By.ID, "list_neq_list").text == "true"
+    assert driver.find_element(By.ID, "list_and_list").text == "[3,4]"
+    assert driver.find_element(By.ID, "list_or_list").text == "[1,2]"
+    assert driver.find_element(By.ID, "list_contains").text == "true"
+    assert driver.find_element(By.ID, "list_reverse").text == "[2,1]"
+
+    # LIST INT
+    assert driver.find_element(By.ID, "list_mult_int").text == "[1,2,1,2,1,2,1,2,1,2]"
+    assert driver.find_element(By.ID, "list_or_int").text == "[1,2]"
+    assert driver.find_element(By.ID, "list_and_int").text == "10"
+    assert driver.find_element(By.ID, "list_eq_int").text == "false"
+    assert driver.find_element(By.ID, "list_neq_int").text == "true"
+
+    # LIST DICT
+    assert driver.find_element(By.ID, "list_and_dict").text == '{"1":2}'
+    assert driver.find_element(By.ID, "list_or_dict").text == "[1,2]"
+    assert driver.find_element(By.ID, "list_eq_dict").text == "false"
+    assert driver.find_element(By.ID, "list_neq_dict").text == "true"
+
+    # DICT, DICT
+    assert driver.find_element(By.ID, "dict_or_dict").text == '{"1":2}'
+    assert driver.find_element(By.ID, "dict_and_dict").text == '{"3":4}'
+    assert driver.find_element(By.ID, "dict_eq_dict").text == "false"
+    assert driver.find_element(By.ID, "dict_neq_dict").text == "true"
+    assert driver.find_element(By.ID, "dict_contains").text == "true"

+ 16 - 0
reflex/.templates/web/utils/state.js

@@ -530,3 +530,19 @@ export const getRefValues = (refs) => {
   // getAttribute is used by RangeSlider because it doesn't assign value
   return refs.map((ref) => ref.current.value || ref.current.getAttribute("aria-valuenow"));
 }
+
+/**
+* Spread two arrays or two objects.
+* @param first The first array or object.
+* @param second The second array or object.
+* @returns The final merged array or object.
+*/
+export const spreadArraysOrObjects = (first, second) => {
+  if (Array.isArray(first) && Array.isArray(second)) {
+    return [...first, ...second];
+  } else if (typeof first === 'object' && typeof second === 'object') {
+    return { ...first, ...second };
+  } else {
+    throw new Error('Both parameters must be either arrays or objects.');
+  }
+}

+ 1 - 0
reflex/compiler/compiler.py

@@ -24,6 +24,7 @@ DEFAULT_IMPORTS: imports.ImportDict = {
         ImportVar(tag="uploadFiles"),
         ImportVar(tag="E"),
         ImportVar(tag="isTrue"),
+        ImportVar(tag="spreadArraysOrObjects"),
         ImportVar(tag="preventDefault"),
         ImportVar(tag="refs"),
         ImportVar(tag="getRefValue"),

+ 229 - 22
reflex/vars.py

@@ -38,6 +38,35 @@ if TYPE_CHECKING:
 # Set of unique variable names.
 USED_VARIABLES = set()
 
+# Supported operators for all types.
+ALL_OPS = ["==", "!=", "!==", "===", "&&", "||"]
+# Delimiters used between function args or operands.
+DELIMITERS = [","]
+# Mapping of valid operations for different type combinations.
+OPERATION_MAPPING = {
+    (int, int): {
+        "+",
+        "-",
+        "/",
+        "//",
+        "*",
+        "%",
+        "**",
+        ">",
+        "<",
+        "<=",
+        ">=",
+        "|",
+        "&",
+    },
+    (int, str): {"*"},
+    (int, list): {"*"},
+    (str, str): {"+", ">", "<", "<=", ">="},
+    (float, float): {"+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="},
+    (float, int): {"+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="},
+    (list, list): {"+", ">", "<", "<=", ">="},
+}
+
 
 def get_unique_variable_name() -> str:
     """Get a unique variable name.
@@ -383,6 +412,7 @@ class Var(ABC):
         type_: Type | None = None,
         flip: bool = False,
         fn: str | None = None,
+        invoke_fn: bool = False,
     ) -> Var:
         """Perform an operation on a var.
 
@@ -392,32 +422,100 @@ class Var(ABC):
             type_: The type of the operation result.
             flip: Whether to flip the order of the operation.
             fn: A function to apply to the operation.
+            invoke_fn: Whether to invoke the function.
 
         Returns:
             The operation result.
+
+        Raises:
+            TypeError: If the operation between two operands is invalid.
+            ValueError: If flip is set to true and value of operand is not provided
         """
-        # Wrap strings in quotes.
         if isinstance(other, str):
             other = Var.create(json.dumps(other))
         else:
             other = Var.create(other)
-        if type_ is None:
-            type_ = self.type_
-        if other is None:
-            name = f"{op}{self.full_name}"
+
+        type_ = type_ or self.type_
+
+        if other is None and flip:
+            raise ValueError(
+                "flip_operands cannot be set to True if the value of 'other' operand is not provided"
+            )
+
+        left_operand, right_operand = (other, self) if flip else (self, other)
+
+        if other is not None:
+            # check if the operation between operands is valid.
+            if op and not self.is_valid_operation(
+                types.get_base_class(left_operand.type_),  # type: ignore
+                types.get_base_class(right_operand.type_),  # type: ignore
+                op,
+            ):
+                raise TypeError(
+                    f"Unsupported Operand type(s) for {op}: `{left_operand.full_name}` of type {left_operand.type_.__name__} and `{right_operand.full_name}` of type {right_operand.type_.__name__}"  # type: ignore
+                )
+
+            # apply function to operands
+            if fn is not None:
+                if invoke_fn:
+                    # invoke the function on left operand.
+                    operation_name = f"{left_operand.full_name}.{fn}({right_operand.full_name})"  # type: ignore
+                else:
+                    # pass the operands as arguments to the function.
+                    operation_name = f"{left_operand.full_name} {op} {right_operand.full_name}"  # type: ignore
+                    operation_name = f"{fn}({operation_name})"
+            else:
+                # apply operator to operands (left operand <operator> right_operand)
+                operation_name = f"{left_operand.full_name} {op} {right_operand.full_name}"  # type: ignore
+                operation_name = format.wrap(operation_name, "(")
         else:
-            props = (other, self) if flip else (self, other)
-            name = f"{props[0].full_name} {op} {props[1].full_name}"
-            if fn is None:
-                name = format.wrap(name, "(")
-        if fn is not None:
-            name = f"{fn}({name})"
+            # apply operator to left operand (<operator> left_operand)
+            operation_name = f"{op}{self.full_name}"
+            # apply function to operands
+            if fn is not None:
+                operation_name = (
+                    f"{fn}({operation_name})"
+                    if not invoke_fn
+                    else f"{self.full_name}.{fn}()"
+                )
+
         return BaseVar(
-            name=name,
+            name=operation_name,
             type_=type_,
             is_local=self.is_local,
         )
 
+    @staticmethod
+    def is_valid_operation(
+        operand1_type: Type, operand2_type: Type, operator: str
+    ) -> bool:
+        """Check if an operation between two operands is valid.
+
+        Args:
+            operand1_type: Type of the operand
+            operand2_type: Type of the second operand
+            operator: The operator.
+
+        Returns:
+            Whether operation is valid or not
+
+        """
+        if operator in ALL_OPS or operator in DELIMITERS:
+            return True
+
+        # bools are subclasses of ints
+        pair = tuple(
+            sorted(
+                [
+                    int if operand1_type == bool else operand1_type,
+                    int if operand2_type == bool else operand2_type,
+                ],
+                key=lambda x: x.__name__,
+            )
+        )
+        return pair in OPERATION_MAPPING and operator in OPERATION_MAPPING[pair]
+
     def compare(self, op: str, other: Var) -> Var:
         """Compare two vars with inequalities.
 
@@ -537,16 +635,26 @@ class Var(ABC):
         """
         return self.compare("<=", other)
 
-    def __add__(self, other: Var) -> Var:
+    def __add__(self, other: Var, flip=False) -> Var:
         """Add two vars.
 
         Args:
             other: The other var to add.
+            flip: Whether to flip operands.
 
         Returns:
             A var representing the sum.
         """
-        return self.operation("+", other)
+        other_type = other.type_ if isinstance(other, Var) else type(other)
+        # For list-list addition, javascript concatenates the content of the lists instead of
+        # merging the list, and for that reason we use the spread operator available through spreadArraysOrObjects
+        # utility function
+        if (
+            types.get_base_class(self.type_) == list
+            and types.get_base_class(other_type) == list
+        ):
+            return self.operation(",", other, fn="spreadArraysOrObjects", flip=flip)
+        return self.operation("+", other, flip=flip)
 
     def __radd__(self, other: Var) -> Var:
         """Add two vars.
@@ -557,7 +665,7 @@ class Var(ABC):
         Returns:
             A var representing the sum.
         """
-        return self.operation("+", other, flip=True)
+        return self.__add__(other=other, flip=True)
 
     def __sub__(self, other: Var) -> Var:
         """Subtract two vars.
@@ -581,15 +689,39 @@ class Var(ABC):
         """
         return self.operation("-", other, flip=True)
 
-    def __mul__(self, other: Var) -> Var:
+    def __mul__(self, other: Var, flip=True) -> Var:
         """Multiply two vars.
 
         Args:
             other: The other var to multiply.
+            flip: Whether to flip operands
 
         Returns:
             A var representing the product.
         """
+        other_type = other.type_ if isinstance(other, Var) else type(other)
+        # For str-int multiplication, we use the repeat function.
+        # i.e "hello" * 2 is equivalent to "hello".repeat(2) in js.
+        if (types.get_base_class(self.type_), types.get_base_class(other_type)) in [
+            (int, str),
+            (str, int),
+        ]:
+            return self.operation(other=other, fn="repeat", invoke_fn=True)
+
+        # For list-int multiplication, we use the Array function.
+        # i.e ["hello"] * 2 is equivalent to Array(2).fill().map(() => ["hello"]).flat() in js.
+        if (types.get_base_class(self.type_), types.get_base_class(other_type)) in [
+            (int, list),
+            (list, int),
+        ]:
+            other_name = other.full_name if isinstance(other, Var) else other
+            name = f"Array({other_name}).fill().map(() => {self.full_name}).flat()"
+            return BaseVar(
+                name=name,
+                type_=str,
+                is_local=self.is_local,
+            )
+
         return self.operation("*", other)
 
     def __rmul__(self, other: Var) -> Var:
@@ -601,7 +733,7 @@ class Var(ABC):
         Returns:
             A var representing the product.
         """
-        return self.operation("*", other, flip=True)
+        return self.__mul__(other=other, flip=True)
 
     def __pow__(self, other: Var) -> Var:
         """Raise a var to a power.
@@ -684,10 +816,29 @@ class Var(ABC):
         """Perform a logical and.
 
         Args:
-            other: The other var to perform the logical and with.
+            other: The other var to perform the logical AND with.
 
         Returns:
-            A var representing the logical and.
+            A var representing the logical AND.
+
+        Note:
+            This method provides behavior specific to JavaScript, where it returns the JavaScript
+            equivalent code (using the '&&' operator) of a logical AND operation.
+            In JavaScript, the
+            logical OR operator '&&' is used for Boolean logic, and this method emulates that behavior
+            by returning the equivalent code as a Var instance.
+
+            In Python, logical AND 'and' operates differently, evaluating expressions immediately, making
+            it challenging to override the behavior entirely.
+            Therefore, this method leverages the
+            bitwise AND '__and__' operator for custom JavaScript-like behavior.
+
+        Example:
+        >>> var1 = Var.create(True)
+        >>> var2 = Var.create(False)
+        >>> js_code = var1 & var2
+        >>> print(js_code.full_name)
+        '(true && false)'
         """
         return self.operation("&&", other, type_=bool)
 
@@ -695,10 +846,29 @@ class Var(ABC):
         """Perform a logical and.
 
         Args:
-            other: The other var to perform the logical and with.
+            other: The other var to perform the logical AND with.
 
         Returns:
-            A var representing the logical and.
+            A var representing the logical AND.
+
+        Note:
+            This method provides behavior specific to JavaScript, where it returns the JavaScript
+            equivalent code (using the '&&' operator) of a logical AND operation.
+            In JavaScript, the
+            logical OR operator '&&' is used for Boolean logic, and this method emulates that behavior
+            by returning the equivalent code as a Var instance.
+
+            In Python, logical AND 'and' operates differently, evaluating expressions immediately, making
+            it challenging to override the behavior entirely.
+            Therefore, this method leverages the
+            bitwise AND '__rand__' operator for custom JavaScript-like behavior.
+
+        Example:
+        >>> var1 = Var.create(True)
+        >>> var2 = Var.create(False)
+        >>> js_code = var1 & var2
+        >>> print(js_code.full_name)
+        '(false && true)'
         """
         return self.operation("&&", other, type_=bool, flip=True)
 
@@ -710,6 +880,23 @@ class Var(ABC):
 
         Returns:
             A var representing the logical or.
+
+        Note:
+            This method provides behavior specific to JavaScript, where it returns the JavaScript
+            equivalent code (using the '||' operator) of a logical OR operation. In JavaScript, the
+            logical OR operator '||' is used for Boolean logic, and this method emulates that behavior
+            by returning the equivalent code as a Var instance.
+
+            In Python, logical OR 'or' operates differently, evaluating expressions immediately, making
+            it challenging to override the behavior entirely. Therefore, this method leverages the
+            bitwise OR '__or__' operator for custom JavaScript-like behavior.
+
+        Example:
+        >>> var1 = Var.create(True)
+        >>> var2 = Var.create(False)
+        >>> js_code = var1 | var2
+        >>> print(js_code.full_name)
+        '(true || false)'
         """
         return self.operation("||", other, type_=bool)
 
@@ -721,6 +908,23 @@ class Var(ABC):
 
         Returns:
             A var representing the logical or.
+
+        Note:
+            This method provides behavior specific to JavaScript, where it returns the JavaScript
+            equivalent code (using the '||' operator) of a logical OR operation. In JavaScript, the
+            logical OR operator '||' is used for Boolean logic, and this method emulates that behavior
+            by returning the equivalent code as a Var instance.
+
+            In Python, logical OR 'or' operates differently, evaluating expressions immediately, making
+            it challenging to override the behavior entirely. Therefore, this method leverages the
+            bitwise OR '__or__' operator for custom JavaScript-like behavior.
+
+        Example:
+        >>> var1 = Var.create(True)
+        >>> var2 = Var.create(False)
+        >>> js_code = var1 | var2
+        >>> print(js_code)
+        'false || true'
         """
         return self.operation("||", other, type_=bool, flip=True)
 
@@ -752,13 +956,16 @@ class Var(ABC):
             raise TypeError(
                 f"Var {self.full_name} of type {self.type_} does not support contains check."
             )
+        method = (
+            "hasOwnProperty" if types.get_base_class(self.type_) == dict else "includes"
+        )
         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})",
+                name=f"{self.full_name}.{method}({other.full_name})",
                 type_=bool,
                 is_local=self.is_local,
             )

+ 412 - 6
tests/test_var.py

@@ -277,6 +277,7 @@ def test_basic_operations(TestObj):
     )
     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()}"
@@ -338,14 +339,17 @@ def test_str_contains(var, expected):
     ],
 )
 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")}}'
+    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(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)}}"
+    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(
@@ -784,3 +788,405 @@ def test_unsupported_default_contains():
         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)