From 773969a264d12dbf416889d9f0d27eccf0dec47e Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 6 Feb 2025 17:59:39 -0800 Subject: [PATCH 1/6] raise error when passing a str(var) --- reflex/components/base/bare.py | 29 +++++++++++++++++++++++++++++ reflex/config.py | 2 +- tests/units/test_var.py | 18 ++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index 0f0bef8b9a7..47d7043a059 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -7,9 +7,33 @@ from reflex.components.component import Component, LiteralComponentVar from reflex.components.tags import Tag from reflex.components.tags.tagless import Tagless +from reflex.config import PerformanceMode, environment +from reflex.utils import console from reflex.utils.imports import ParsedImportDict from reflex.vars import BooleanVar, ObjectVar, Var from reflex.vars.base import VarData +from reflex.vars.sequence import LiteralStringVar + + +def validate_str(value: str): + """Validate a string value. + + Args: + value: The value to validate. + + Raises: + ValueError: If the value is a Var and the performance mode is set to raise. + """ + perf_mode = environment.REFLEX_PERF_MODE.get() + if perf_mode != PerformanceMode.OFF and value.startswith("reflex___state"): + if perf_mode == PerformanceMode.WARN: + console.warn( + f"Output includes {value!s} which will be displayed as a string. If you are calling `str` on a Var, consider using .to_string() instead." + ) + elif perf_mode == PerformanceMode.RAISE: + raise ValueError( + f"Output includes {value!s} which will be displayed as a string. If you are calling `str` on a Var, consider using .to_string() instead." + ) class Bare(Component): @@ -28,9 +52,14 @@ def create(cls, contents: Any) -> Component: The component. """ if isinstance(contents, Var): + if isinstance(contents, LiteralStringVar): + validate_str(contents._var_value) return cls(contents=contents) else: + if isinstance(contents, str): + validate_str(contents) contents = str(contents) if contents is not None else "" + return cls(contents=contents) def _get_all_hooks_internal(self) -> dict[str, VarData | None]: diff --git a/reflex/config.py b/reflex/config.py index f3d40dc373c..7d26eaeba4f 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -563,7 +563,7 @@ class EnvironmentVariables: REFLEX_CHECK_LATEST_VERSION: EnvVar[bool] = env_var(True) # In which performance mode to run the app. - REFLEX_PERF_MODE: EnvVar[Optional[PerformanceMode]] = env_var(PerformanceMode.WARN) + REFLEX_PERF_MODE: EnvVar[PerformanceMode] = env_var(PerformanceMode.WARN) # The maximum size of the reflex state in kilobytes. REFLEX_STATE_SIZE_LIMIT: EnvVar[int] = env_var(1000) diff --git a/tests/units/test_var.py b/tests/units/test_var.py index a72242814ae..35feb03d7de 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -8,6 +8,7 @@ import reflex as rx from reflex.base import Base +from reflex.config import PerformanceMode from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG from reflex.state import BaseState from reflex.utils.exceptions import ( @@ -1893,3 +1894,20 @@ def test_var_data_hooks(): def test_var_data_with_hooks_value(): var_data = VarData(hooks={"what": VarData(hooks={"whot": VarData(hooks="whott")})}) assert var_data == VarData(hooks=["what", "whot", "whott"]) + + +def test_str_var_in_components(): + from reflex.config import environment + + current_performance_mode = environment.REFLEX_PERF_MODE.get() + environment.REFLEX_PERF_MODE.set(PerformanceMode.RAISE) + + class StateWithVar(rx.State): + field: int = 1 + + with pytest.raises(ValueError): + rx.vstack( + str(StateWithVar.field), + ) + + environment.REFLEX_PERF_MODE.set(current_performance_mode) From e3e983afc4eff7cb79605af13f91a6f660a6095a Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 6 Feb 2025 18:14:42 -0800 Subject: [PATCH 2/6] make it faster --- reflex/components/base/bare.py | 13 ++++++++++++- reflex/utils/decorator.py | 25 +++++++++++++++++++++++++ tests/units/test_var.py | 21 ++++++++++++++------- 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 reflex/utils/decorator.py diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index 47d7043a059..24a3b9f6a4c 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -9,12 +9,23 @@ from reflex.components.tags.tagless import Tagless from reflex.config import PerformanceMode, environment from reflex.utils import console +from reflex.utils.decorator import once from reflex.utils.imports import ParsedImportDict from reflex.vars import BooleanVar, ObjectVar, Var from reflex.vars.base import VarData from reflex.vars.sequence import LiteralStringVar +@once +def performace_mode(): + """Get the performance mode. + + Returns: + The performance mode. + """ + return environment.REFLEX_PERF_MODE.get() + + def validate_str(value: str): """Validate a string value. @@ -24,7 +35,7 @@ def validate_str(value: str): Raises: ValueError: If the value is a Var and the performance mode is set to raise. """ - perf_mode = environment.REFLEX_PERF_MODE.get() + perf_mode = performace_mode() if perf_mode != PerformanceMode.OFF and value.startswith("reflex___state"): if perf_mode == PerformanceMode.WARN: console.warn( diff --git a/reflex/utils/decorator.py b/reflex/utils/decorator.py new file mode 100644 index 00000000000..5c9c0bf3a2c --- /dev/null +++ b/reflex/utils/decorator.py @@ -0,0 +1,25 @@ +"""Decorator utilities.""" + +from typing import Callable, TypeVar + +T = TypeVar("T") + + +def once(f: Callable[[], T]) -> Callable[[], T]: + """A decorator that calls the function once and caches the result. + + Args: + f: The function to call. + + Returns: + A function that calls the function once and caches the result. + """ + unset = object() + value: object | T = unset + + def wrapper() -> T: + nonlocal value + value = f() if value is unset else value + return value # pyright: ignore[reportReturnType] + + return wrapper diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 35feb03d7de..ff29d42900b 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1896,18 +1896,25 @@ def test_var_data_with_hooks_value(): assert var_data == VarData(hooks=["what", "whot", "whott"]) -def test_str_var_in_components(): - from reflex.config import environment - - current_performance_mode = environment.REFLEX_PERF_MODE.get() - environment.REFLEX_PERF_MODE.set(PerformanceMode.RAISE) - +def test_str_var_in_components(mocker): class StateWithVar(rx.State): field: int = 1 + mocker.patch( + "reflex.components.base.bare.performace_mode", + lambda: PerformanceMode.RAISE, + ) + with pytest.raises(ValueError): rx.vstack( str(StateWithVar.field), ) - environment.REFLEX_PERF_MODE.set(current_performance_mode) + mocker.patch( + "reflex.components.base.bare.performace_mode", + lambda: PerformanceMode.OFF, + ) + + rx.vstack( + str(StateWithVar.field), + ) From 370cf67b16ced56dedd2bcb9dcbd10a4d4cb6009 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 7 Feb 2025 12:06:02 -0800 Subject: [PATCH 3/6] fix typo --- reflex/components/base/bare.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py index 24a3b9f6a4c..73b0680d358 100644 --- a/reflex/components/base/bare.py +++ b/reflex/components/base/bare.py @@ -17,7 +17,7 @@ @once -def performace_mode(): +def get_performance_mode(): """Get the performance mode. Returns: @@ -35,7 +35,7 @@ def validate_str(value: str): Raises: ValueError: If the value is a Var and the performance mode is set to raise. """ - perf_mode = performace_mode() + perf_mode = get_performance_mode() if perf_mode != PerformanceMode.OFF and value.startswith("reflex___state"): if perf_mode == PerformanceMode.WARN: console.warn( From 43e850ee26df064f4d78966f4e7f9356d4d4adb7 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Fri, 7 Feb 2025 12:09:23 -0800 Subject: [PATCH 4/6] fix tests --- tests/units/test_var.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/units/test_var.py b/tests/units/test_var.py index ff29d42900b..5ea804e74be 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1901,7 +1901,7 @@ class StateWithVar(rx.State): field: int = 1 mocker.patch( - "reflex.components.base.bare.performace_mode", + "reflex.components.base.bare.get_performance_mode", lambda: PerformanceMode.RAISE, ) @@ -1911,7 +1911,7 @@ class StateWithVar(rx.State): ) mocker.patch( - "reflex.components.base.bare.performace_mode", + "reflex.components.base.bare.get_performance_mode", lambda: PerformanceMode.OFF, ) From 0099c8ac5f0286533a271a20c57f7679289f6e02 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 10 Feb 2025 14:25:52 -0800 Subject: [PATCH 5/6] mocker consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Thomas Brandého --- tests/units/test_var.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/units/test_var.py b/tests/units/test_var.py index 5ea804e74be..dedadc80240 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1902,7 +1902,7 @@ class StateWithVar(rx.State): mocker.patch( "reflex.components.base.bare.get_performance_mode", - lambda: PerformanceMode.RAISE, + return_value=PerformanceMode.RAISE, ) with pytest.raises(ValueError): From ac923f1bfc63a0bea5a21e84cdcb4ab9078d7958 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 10 Feb 2025 14:26:03 -0800 Subject: [PATCH 6/6] ditto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Thomas Brandého --- tests/units/test_var.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/units/test_var.py b/tests/units/test_var.py index dedadc80240..8fcd288e676 100644 --- a/tests/units/test_var.py +++ b/tests/units/test_var.py @@ -1912,7 +1912,7 @@ class StateWithVar(rx.State): mocker.patch( "reflex.components.base.bare.get_performance_mode", - lambda: PerformanceMode.OFF, + return_value=PerformanceMode.OFF, ) rx.vstack(