Merge branch 'main' into add-validation-to-function-vars
This commit is contained in:
commit
a2074b9081
@ -46,10 +46,26 @@ def render_multiple_pages(app, num: int):
|
||||
class State(rx.State):
|
||||
"""The app state."""
|
||||
|
||||
position: str
|
||||
college: str
|
||||
age: Tuple[int, int] = (18, 50)
|
||||
salary: Tuple[int, int] = (0, 25000000)
|
||||
position: rx.Field[str]
|
||||
college: rx.Field[str]
|
||||
age: rx.Field[Tuple[int, int]] = rx.field((18, 50))
|
||||
salary: rx.Field[Tuple[int, int]] = rx.field((0, 25000000))
|
||||
|
||||
@rx.event
|
||||
def set_position(self, value: str):
|
||||
self.position = value
|
||||
|
||||
@rx.event
|
||||
def set_college(self, value: str):
|
||||
self.college = value
|
||||
|
||||
@rx.event
|
||||
def set_age(self, value: list[int]):
|
||||
self.age = (value[0], value[1])
|
||||
|
||||
@rx.event
|
||||
def set_salary(self, value: list[int]):
|
||||
self.salary = (value[0], value[1])
|
||||
|
||||
comp1 = rx.center(
|
||||
rx.theme_panel(),
|
||||
@ -74,13 +90,13 @@ def render_multiple_pages(app, num: int):
|
||||
rx.select(
|
||||
["C", "PF", "SF", "PG", "SG"],
|
||||
placeholder="Select a position. (All)",
|
||||
on_change=State.set_position, # pyright: ignore [reportAttributeAccessIssue]
|
||||
on_change=State.set_position,
|
||||
size="3",
|
||||
),
|
||||
rx.select(
|
||||
college,
|
||||
placeholder="Select a college. (All)",
|
||||
on_change=State.set_college, # pyright: ignore [reportAttributeAccessIssue]
|
||||
on_change=State.set_college,
|
||||
size="3",
|
||||
),
|
||||
),
|
||||
@ -95,7 +111,7 @@ def render_multiple_pages(app, num: int):
|
||||
default_value=[18, 50],
|
||||
min=18,
|
||||
max=50,
|
||||
on_value_commit=State.set_age, # pyright: ignore [reportAttributeAccessIssue]
|
||||
on_value_commit=State.set_age,
|
||||
),
|
||||
align_items="left",
|
||||
width="100%",
|
||||
@ -110,7 +126,7 @@ def render_multiple_pages(app, num: int):
|
||||
default_value=[0, 25000000],
|
||||
min=0,
|
||||
max=25000000,
|
||||
on_value_commit=State.set_salary, # pyright: ignore [reportAttributeAccessIssue]
|
||||
on_value_commit=State.set_salary,
|
||||
),
|
||||
align_items="left",
|
||||
width="100%",
|
||||
|
@ -591,7 +591,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
Returns:
|
||||
The generated component.
|
||||
"""
|
||||
return component if isinstance(component, Component) else component()
|
||||
from reflex.compiler.compiler import into_component
|
||||
|
||||
return into_component(component)
|
||||
|
||||
def add_page(
|
||||
self,
|
||||
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Callable, Dict, Iterable, Optional, Tuple, Type, Union
|
||||
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Sequence, Tuple, Type, Union
|
||||
|
||||
from reflex import constants
|
||||
from reflex.compiler import templates, utils
|
||||
@ -545,30 +545,47 @@ def purge_web_pages_dir():
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from reflex.app import UnevaluatedPage
|
||||
|
||||
COMPONENT_TYPE = Union[Component, Var, Tuple[Union[Component, Var], ...]]
|
||||
COMPONENT_TYPE_OR_CALLABLE = Union[COMPONENT_TYPE, Callable[[], COMPONENT_TYPE]]
|
||||
from reflex.app import ComponentCallable, UnevaluatedPage
|
||||
|
||||
|
||||
def componentify_unevaluated(
|
||||
possible_component: COMPONENT_TYPE_OR_CALLABLE,
|
||||
) -> Component:
|
||||
"""Convert a possible component to a component.
|
||||
def _into_component_once(component: Component | ComponentCallable) -> Component | None:
|
||||
"""Convert a component to a Component.
|
||||
|
||||
Args:
|
||||
possible_component: The possible component to convert.
|
||||
component: The component to convert.
|
||||
|
||||
Returns:
|
||||
The component.
|
||||
The converted component.
|
||||
"""
|
||||
if isinstance(possible_component, Var):
|
||||
return Fragment.create(possible_component)
|
||||
if isinstance(possible_component, tuple):
|
||||
return Fragment.create(*possible_component)
|
||||
if isinstance(possible_component, Component):
|
||||
return possible_component
|
||||
return componentify_unevaluated(possible_component())
|
||||
if isinstance(component, Component):
|
||||
return component
|
||||
if isinstance(component, (Var, int, float, str)):
|
||||
return Fragment.create(component)
|
||||
if isinstance(component, Sequence):
|
||||
return Fragment.create(*component)
|
||||
return None
|
||||
|
||||
|
||||
def into_component(component: Component | ComponentCallable) -> Component:
|
||||
"""Convert a component to a Component.
|
||||
|
||||
Args:
|
||||
component: The component to convert.
|
||||
|
||||
Returns:
|
||||
The converted component.
|
||||
|
||||
Raises:
|
||||
TypeError: If the component is not a Component.
|
||||
"""
|
||||
if (converted := _into_component_once(component)) is not None:
|
||||
return converted
|
||||
if (
|
||||
callable(component)
|
||||
and (converted := _into_component_once(component())) is not None
|
||||
):
|
||||
return converted
|
||||
raise TypeError(f"Expected a Component, got {type(component)}")
|
||||
|
||||
|
||||
def compile_unevaluated_page(
|
||||
@ -591,7 +608,7 @@ def compile_unevaluated_page(
|
||||
The compiled component and whether state should be enabled.
|
||||
"""
|
||||
# Generate the component if it is a callable.
|
||||
component = componentify_unevaluated(page.component)
|
||||
component = into_component(page.component)
|
||||
|
||||
component._add_style_recursive(style or {}, theme)
|
||||
|
||||
@ -696,7 +713,7 @@ class ExecutorSafeFunctions:
|
||||
The route, compiled component, and compiled page.
|
||||
"""
|
||||
component, enable_state = compile_unevaluated_page(
|
||||
route, cls.UNCOMPILED_PAGES[route]
|
||||
route, cls.UNCOMPILED_PAGES[route], cls.STATE, style, theme
|
||||
)
|
||||
return route, component, compile_page(route, component, cls.STATE)
|
||||
|
||||
|
@ -1742,6 +1742,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
|
||||
Yields:
|
||||
StateUpdate object
|
||||
|
||||
Raises:
|
||||
ValueError: If a string value is received for an int or float type and cannot be converted.
|
||||
"""
|
||||
from reflex.utils import telemetry
|
||||
|
||||
@ -1779,12 +1782,25 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
hinted_args, (Base, BaseModelV1, BaseModelV2)
|
||||
):
|
||||
payload[arg] = hinted_args(**value)
|
||||
if isinstance(value, list) and (hinted_args is set or hinted_args is Set):
|
||||
elif isinstance(value, list) and (hinted_args is set or hinted_args is Set):
|
||||
payload[arg] = set(value)
|
||||
if isinstance(value, list) and (
|
||||
elif isinstance(value, list) and (
|
||||
hinted_args is tuple or hinted_args is Tuple
|
||||
):
|
||||
payload[arg] = tuple(value)
|
||||
elif isinstance(value, str) and (
|
||||
hinted_args is int or hinted_args is float
|
||||
):
|
||||
try:
|
||||
payload[arg] = hinted_args(value)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Received a string value ({value}) for {arg} but expected a {hinted_args}"
|
||||
) from None
|
||||
else:
|
||||
console.warn(
|
||||
f"Received a string value ({value}) for {arg} but expected a {hinted_args}. A simple conversion was successful."
|
||||
)
|
||||
|
||||
# Wrap the function in a try/except block.
|
||||
try:
|
||||
@ -2459,7 +2475,7 @@ class ComponentState(State, mixin=True):
|
||||
Returns:
|
||||
A new instance of the Component with an independent copy of the State.
|
||||
"""
|
||||
from reflex.compiler.compiler import componentify_unevaluated
|
||||
from reflex.compiler.compiler import into_component
|
||||
|
||||
cls._per_component_state_instance_count += 1
|
||||
state_cls_name = f"{cls.__name__}_n{cls._per_component_state_instance_count}"
|
||||
@ -2472,7 +2488,7 @@ class ComponentState(State, mixin=True):
|
||||
# Save a reference to the dynamic state for pickle/unpickle.
|
||||
setattr(reflex.istate.dynamic, state_cls_name, component_state)
|
||||
component = component_state.get_component(*children, **props)
|
||||
component = componentify_unevaluated(component)
|
||||
component = into_component(component)
|
||||
component.State = component_state
|
||||
return component
|
||||
|
||||
|
@ -1050,7 +1050,7 @@ class Var(Generic[VAR_TYPE]):
|
||||
"""
|
||||
actual_name = self._var_field_name
|
||||
|
||||
def setter(state: BaseState, value: Any):
|
||||
def setter(state: Any, value: Any):
|
||||
"""Get the setter for the var.
|
||||
|
||||
Args:
|
||||
@ -1068,6 +1068,8 @@ class Var(Generic[VAR_TYPE]):
|
||||
else:
|
||||
setattr(state, actual_name, value)
|
||||
|
||||
setter.__annotations__["value"] = self._var_type
|
||||
|
||||
setter.__qualname__ = self._get_setter_name()
|
||||
|
||||
return setter
|
||||
|
@ -20,7 +20,11 @@ def BackgroundTask():
|
||||
class State(rx.State):
|
||||
counter: int = 0
|
||||
_task_id: int = 0
|
||||
iterations: int = 10
|
||||
iterations: rx.Field[int] = rx.field(10)
|
||||
|
||||
@rx.event
|
||||
def set_iterations(self, value: str):
|
||||
self.iterations = int(value)
|
||||
|
||||
@rx.event(background=True)
|
||||
async def handle_event(self):
|
||||
@ -125,8 +129,8 @@ def BackgroundTask():
|
||||
rx.input(
|
||||
id="iterations",
|
||||
placeholder="Iterations",
|
||||
value=State.iterations.to_string(), # pyright: ignore [reportAttributeAccessIssue]
|
||||
on_change=State.set_iterations, # pyright: ignore [reportAttributeAccessIssue]
|
||||
value=State.iterations.to_string(),
|
||||
on_change=State.set_iterations,
|
||||
),
|
||||
rx.button(
|
||||
"Delayed Increment",
|
||||
|
Loading…
Reference in New Issue
Block a user