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