From 6e6a3063421c8312bae0bcf219c23d75b89c8376 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 17 Oct 2024 14:49:25 -0700 Subject: [PATCH 1/3] implement rx dynamic --- reflex/__init__.py | 1 + reflex/__init__.pyi | 1 + reflex/state.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/reflex/__init__.py b/reflex/__init__.py index ad51d2cf45..8ae54f4760 100644 --- a/reflex/__init__.py +++ b/reflex/__init__.py @@ -329,6 +329,7 @@ "SessionStorage", "ComponentState", "State", + "dynamic", ], "style": ["Style", "toggle_color_mode"], "utils.imports": ["ImportVar"], diff --git a/reflex/__init__.pyi b/reflex/__init__.pyi index d928778d82..2e87280279 100644 --- a/reflex/__init__.pyi +++ b/reflex/__init__.pyi @@ -184,6 +184,7 @@ from .state import Cookie as Cookie from .state import LocalStorage as LocalStorage from .state import SessionStorage as SessionStorage from .state import State as State +from .state import dynamic as dynamic from .state import var as var from .style import Style as Style from .style import toggle_color_mode as toggle_color_mode diff --git a/reflex/state.py b/reflex/state.py index 0d6eed0edc..5eeed36cc1 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -31,6 +31,7 @@ Set, Tuple, Type, + TypeVar, Union, cast, get_args, @@ -2091,6 +2092,47 @@ class State(BaseState): is_hydrated: bool = False +T = TypeVar("T", bound=BaseState) + + +def dynamic(func: Callable[[T], Component]): + """Create a dynamically generated components from a state class. + + Args: + func: The function to generate the component. + + Returns: + The dynamically generated component. + """ + number_of_parameters = len(inspect.signature(func).parameters) + + func_signature = get_type_hints(func) + + if "return" in func_signature: + func_signature.pop("return") + + values = list(func_signature.values()) + + if number_of_parameters != 1: + raise ValueError( + "The function must have exactly one parameter, which is the state class." + ) + + if len(values) != 1: + raise ValueError( + "You must provide a type hint for the state class in the function." + ) + + state_class: Type[T] = values[0] + + def wrapper() -> Component: + from reflex.components.base.fragment import fragment + + return fragment(state_class._evaluate(lambda state: func(state))) + + return wrapper + + class FrontendEventExceptionState(State): """Substate for handling frontend exceptions.""" From 89ea0de4c5566f8163495c772789640fe6ceb67c Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Thu, 17 Oct 2024 14:54:29 -0700 Subject: [PATCH 2/3] dang it darglint --- reflex/state.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reflex/state.py b/reflex/state.py index 5eeed36cc1..fb05d558f9 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -2103,6 +2103,10 @@ def dynamic(func: Callable[[T], Component]): Returns: The dynamically generated component. + + Raises: + ValueError: If the function does not have exactly one parameter. + ValueError: If the function does not have a type hint for the state class. """ number_of_parameters = len(inspect.signature(func).parameters) From a7eae533099eca6b680b1c85b4c7402462b411d1 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Mon, 21 Oct 2024 12:36:41 -0700 Subject: [PATCH 3/3] add custom type --- reflex/state.py | 9 +++++---- reflex/utils/exceptions.py | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/reflex/state.py b/reflex/state.py index fb05d558f9..8fb18e8801 100644 --- a/reflex/state.py +++ b/reflex/state.py @@ -76,6 +76,7 @@ from reflex.utils.exceptions import ( ComputedVarShadowsBaseVars, ComputedVarShadowsStateVar, + DynamicComponentInvalidSignature, DynamicRouteArgShadowsStateVar, EventHandlerShadowsBuiltInStateMethod, ImmutableStateError, @@ -2105,8 +2106,8 @@ def dynamic(func: Callable[[T], Component]): The dynamically generated component. Raises: - ValueError: If the function does not have exactly one parameter. - ValueError: If the function does not have a type hint for the state class. + DynamicComponentInvalidSignature: If the function does not have exactly one parameter. + DynamicComponentInvalidSignature: If the function does not have a type hint for the state class. """ number_of_parameters = len(inspect.signature(func).parameters) @@ -2118,12 +2119,12 @@ def dynamic(func: Callable[[T], Component]): values = list(func_signature.values()) if number_of_parameters != 1: - raise ValueError( + raise DynamicComponentInvalidSignature( "The function must have exactly one parameter, which is the state class." ) if len(values) != 1: - raise ValueError( + raise DynamicComponentInvalidSignature( "You must provide a type hint for the state class in the function." ) diff --git a/reflex/utils/exceptions.py b/reflex/utils/exceptions.py index 35f59a0e14..f74c078fee 100644 --- a/reflex/utils/exceptions.py +++ b/reflex/utils/exceptions.py @@ -135,3 +135,7 @@ class SetUndefinedStateVarError(ReflexError, AttributeError): class StateSchemaMismatchError(ReflexError, TypeError): """Raised when the serialized schema of a state class does not match the current schema.""" + + +class DynamicComponentInvalidSignature(ReflexError, TypeError): + """Raised when a dynamic component has an invalid signature."""