This commit is contained in:
Khaleel Al-Adhami 2024-10-16 15:25:06 -07:00
commit 2c8f5d7e7a
21 changed files with 225 additions and 185 deletions

View File

@ -14,7 +14,7 @@ env:
jobs: jobs:
check_latest_node: check_latest_node:
runs-on: ubuntu-latest runs-on: ubuntu-22.04
strategy: strategy:
matrix: matrix:
python-version: ['3.12'] python-version: ['3.12']

View File

@ -24,7 +24,7 @@ jobs:
matrix: matrix:
state_manager: ['redis', 'memory'] state_manager: ['redis', 'memory']
python-version: ['3.11.5', '3.12.0'] python-version: ['3.11.5', '3.12.0']
runs-on: ubuntu-latest runs-on: ubuntu-22.04
services: services:
# Label used to access the service container # Label used to access the service container
redis: redis:

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "reflex" name = "reflex"
version = "0.6.3dev1" version = "0.6.4dev1"
description = "Web apps in pure Python." description = "Web apps in pure Python."
license = "Apache-2.0" license = "Apache-2.0"
authors = [ authors = [

View File

@ -31,7 +31,7 @@ class ErrorBoundary(Component):
on_click: Optional[EventType[[]]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None,
on_double_click: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None,
on_error: Optional[EventType[[]]] = None, on_error: Optional[EventType] = None,
on_focus: Optional[EventType[[]]] = None, on_focus: Optional[EventType[[]]] = None,
on_mount: Optional[EventType[[]]] = None, on_mount: Optional[EventType[[]]] = None,
on_mouse_down: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None,

View File

@ -45,6 +45,7 @@ from reflex.event import (
EventVar, EventVar,
call_event_fn, call_event_fn,
call_event_handler, call_event_handler,
empty_event,
get_handler_args, get_handler_args,
) )
from reflex.style import Style, format_as_emotion from reflex.style import Style, format_as_emotion
@ -623,21 +624,21 @@ class Component(BaseComponent, ABC):
""" """
default_triggers = { default_triggers = {
EventTriggers.ON_FOCUS: lambda: [], EventTriggers.ON_FOCUS: empty_event,
EventTriggers.ON_BLUR: lambda: [], EventTriggers.ON_BLUR: empty_event,
EventTriggers.ON_CLICK: lambda: [], EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_CONTEXT_MENU: lambda: [], EventTriggers.ON_CONTEXT_MENU: empty_event,
EventTriggers.ON_DOUBLE_CLICK: lambda: [], EventTriggers.ON_DOUBLE_CLICK: empty_event,
EventTriggers.ON_MOUSE_DOWN: lambda: [], EventTriggers.ON_MOUSE_DOWN: empty_event,
EventTriggers.ON_MOUSE_ENTER: lambda: [], EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: lambda: [], EventTriggers.ON_MOUSE_LEAVE: empty_event,
EventTriggers.ON_MOUSE_MOVE: lambda: [], EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OUT: lambda: [], EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_OVER: lambda: [], EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_UP: lambda: [], EventTriggers.ON_MOUSE_UP: empty_event,
EventTriggers.ON_SCROLL: lambda: [], EventTriggers.ON_SCROLL: empty_event,
EventTriggers.ON_MOUNT: lambda: [], EventTriggers.ON_MOUNT: empty_event,
EventTriggers.ON_UNMOUNT: lambda: [], EventTriggers.ON_UNMOUNT: empty_event,
} }
# Look for component specific triggers, # Look for component specific triggers,
@ -648,7 +649,7 @@ class Component(BaseComponent, ABC):
annotation = field.annotation annotation = field.annotation
if (metadata := getattr(annotation, "__metadata__", None)) is not None: if (metadata := getattr(annotation, "__metadata__", None)) is not None:
args_spec = metadata[0] args_spec = metadata[0]
default_triggers[field.name] = args_spec or (lambda: []) default_triggers[field.name] = args_spec or (empty_event) # type: ignore
return default_triggers return default_triggers
def __repr__(self) -> str: def __repr__(self) -> str:
@ -1705,7 +1706,7 @@ class CustomComponent(Component):
value = self._create_event_chain( value = self._create_event_chain(
value=value, value=value,
args_spec=event_triggers_in_component_declaration.get( args_spec=event_triggers_in_component_declaration.get(
key, lambda: [] key, empty_event
), ),
) )
self.props[format.to_camel_case(key)] = value self.props[format.to_camel_case(key)] = value

View File

@ -139,20 +139,20 @@ class DataEditor(NoSSRComponent):
on_cell_activated: Optional[EventType] = None, on_cell_activated: Optional[EventType] = None,
on_cell_clicked: Optional[EventType] = None, on_cell_clicked: Optional[EventType] = None,
on_cell_context_menu: Optional[EventType] = None, on_cell_context_menu: Optional[EventType] = None,
on_cell_edited: Optional[EventType[[]]] = None, on_cell_edited: Optional[EventType] = None,
on_click: Optional[EventType[[]]] = None, on_click: Optional[EventType[[]]] = None,
on_column_resize: Optional[EventType[[]]] = None, on_column_resize: Optional[EventType] = None,
on_context_menu: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None,
on_delete: Optional[EventType[[]]] = None, on_delete: Optional[EventType] = None,
on_double_click: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None,
on_finished_editing: Optional[EventType[[]]] = None, on_finished_editing: Optional[EventType] = None,
on_focus: Optional[EventType[[]]] = None, on_focus: Optional[EventType[[]]] = None,
on_group_header_clicked: Optional[EventType[[]]] = None, on_group_header_clicked: Optional[EventType] = None,
on_group_header_context_menu: Optional[EventType[[]]] = None, on_group_header_context_menu: Optional[EventType] = None,
on_group_header_renamed: Optional[EventType[[]]] = None, on_group_header_renamed: Optional[EventType] = None,
on_header_clicked: Optional[EventType] = None, on_header_clicked: Optional[EventType] = None,
on_header_context_menu: Optional[EventType] = None, on_header_context_menu: Optional[EventType] = None,
on_header_menu_click: Optional[EventType[[]]] = None, on_header_menu_click: Optional[EventType] = None,
on_item_hovered: Optional[EventType] = None, on_item_hovered: Optional[EventType] = None,
on_mount: Optional[EventType[[]]] = None, on_mount: Optional[EventType[[]]] = None,
on_mouse_down: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None,

View File

@ -58,7 +58,7 @@ class Audio(ReactPlayer):
on_play: Optional[EventType[[]]] = None, on_play: Optional[EventType[[]]] = None,
on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None,
on_playback_rate_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None,
on_progress: Optional[EventType[[]]] = None, on_progress: Optional[EventType] = None,
on_ready: Optional[EventType[[]]] = None, on_ready: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_seek: Optional[EventType] = None, on_seek: Optional[EventType] = None,

View File

@ -56,7 +56,7 @@ class ReactPlayer(NoSSRComponent):
on_play: Optional[EventType[[]]] = None, on_play: Optional[EventType[[]]] = None,
on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None,
on_playback_rate_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None,
on_progress: Optional[EventType[[]]] = None, on_progress: Optional[EventType] = None,
on_ready: Optional[EventType[[]]] = None, on_ready: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_seek: Optional[EventType] = None, on_seek: Optional[EventType] = None,

View File

@ -58,7 +58,7 @@ class Video(ReactPlayer):
on_play: Optional[EventType[[]]] = None, on_play: Optional[EventType[[]]] = None,
on_playback_quality_change: Optional[EventType[[]]] = None, on_playback_quality_change: Optional[EventType[[]]] = None,
on_playback_rate_change: Optional[EventType[[]]] = None, on_playback_rate_change: Optional[EventType[[]]] = None,
on_progress: Optional[EventType[[]]] = None, on_progress: Optional[EventType] = None,
on_ready: Optional[EventType[[]]] = None, on_ready: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_seek: Optional[EventType] = None, on_seek: Optional[EventType] = None,

View File

@ -252,7 +252,7 @@ class Brush(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CHANGE: lambda: [], EventTriggers.ON_CHANGE: empty_event,
} }
@ -293,10 +293,10 @@ class Cartesian(Recharts):
name: Var[Union[str, int]] name: Var[Union[str, int]]
# The customized event handler of animation start # The customized event handler of animation start
on_animation_start: EventHandler[lambda: []] on_animation_start: EventHandler[empty_event]
# The customized event handler of animation end # The customized event handler of animation end
on_animation_end: EventHandler[lambda: []] on_animation_end: EventHandler[empty_event]
# The customized event handler of click on the component in this group # The customized event handler of click on the component in this group
on_click: EventHandler[empty_event] on_click: EventHandler[empty_event]

View File

@ -330,9 +330,9 @@ class RadarChart(ChartBase):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CLICK: lambda: [], EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_ENTER: lambda: [], EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: lambda: [], EventTriggers.ON_MOUSE_LEAVE: empty_event,
} }
@ -419,14 +419,14 @@ class ScatterChart(ChartBase):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CLICK: lambda: [], EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_DOWN: lambda: [], EventTriggers.ON_MOUSE_DOWN: empty_event,
EventTriggers.ON_MOUSE_UP: lambda: [], EventTriggers.ON_MOUSE_UP: empty_event,
EventTriggers.ON_MOUSE_MOVE: lambda: [], EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OVER: lambda: [], EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_OUT: lambda: [], EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_ENTER: lambda: [], EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: lambda: [], EventTriggers.ON_MOUSE_LEAVE: empty_event,
} }

View File

@ -103,14 +103,14 @@ class Pie(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_ANIMATION_START: lambda: [], EventTriggers.ON_ANIMATION_START: empty_event,
EventTriggers.ON_ANIMATION_END: lambda: [], EventTriggers.ON_ANIMATION_END: empty_event,
EventTriggers.ON_CLICK: lambda: [], EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_MOVE: lambda: [], EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OVER: lambda: [], EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_OUT: lambda: [], EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_ENTER: lambda: [], EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: lambda: [], EventTriggers.ON_MOUSE_LEAVE: empty_event,
} }
@ -167,8 +167,8 @@ class Radar(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_ANIMATION_START: lambda: [], EventTriggers.ON_ANIMATION_START: empty_event,
EventTriggers.ON_ANIMATION_END: lambda: [], EventTriggers.ON_ANIMATION_END: empty_event,
} }
@ -219,14 +219,14 @@ class RadialBar(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CLICK: lambda: [], EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_MOVE: lambda: [], EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OVER: lambda: [], EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_OUT: lambda: [], EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_ENTER: lambda: [], EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: lambda: [], EventTriggers.ON_MOUSE_LEAVE: empty_event,
EventTriggers.ON_ANIMATION_START: lambda: [], EventTriggers.ON_ANIMATION_START: empty_event,
EventTriggers.ON_ANIMATION_END: lambda: [], EventTriggers.ON_ANIMATION_END: empty_event,
} }
@ -392,12 +392,12 @@ class PolarRadiusAxis(Recharts):
A dict mapping the event trigger to the var that is passed to the handler. A dict mapping the event trigger to the var that is passed to the handler.
""" """
return { return {
EventTriggers.ON_CLICK: lambda: [], EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_MOVE: lambda: [], EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OVER: lambda: [], EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_OUT: lambda: [], EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_ENTER: lambda: [], EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: lambda: [], EventTriggers.ON_MOUSE_LEAVE: empty_event,
} }

View File

@ -122,16 +122,16 @@ class Editor(NoSSRComponent):
class_name: Optional[Any] = None, class_name: Optional[Any] = None,
autofocus: Optional[bool] = None, autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[EventType[[]]] = None, on_blur: Optional[EventType] = None,
on_change: Optional[EventType[[]]] = None, on_change: Optional[EventType] = None,
on_click: Optional[EventType[[]]] = None, on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[EventType[[]]] = None, on_context_menu: Optional[EventType[[]]] = None,
on_copy: Optional[EventType[[]]] = None, on_copy: Optional[EventType] = None,
on_cut: Optional[EventType[[]]] = None, on_cut: Optional[EventType] = None,
on_double_click: Optional[EventType[[]]] = None, on_double_click: Optional[EventType[[]]] = None,
on_focus: Optional[EventType[[]]] = None, on_focus: Optional[EventType[[]]] = None,
on_input: Optional[EventType[[]]] = None, on_input: Optional[EventType] = None,
on_load: Optional[EventType[[]]] = None, on_load: Optional[EventType] = None,
on_mount: Optional[EventType[[]]] = None, on_mount: Optional[EventType[[]]] = None,
on_mouse_down: Optional[EventType[[]]] = None, on_mouse_down: Optional[EventType[[]]] = None,
on_mouse_enter: Optional[EventType[[]]] = None, on_mouse_enter: Optional[EventType[[]]] = None,
@ -140,12 +140,12 @@ class Editor(NoSSRComponent):
on_mouse_out: Optional[EventType[[]]] = None, on_mouse_out: Optional[EventType[[]]] = None,
on_mouse_over: Optional[EventType[[]]] = None, on_mouse_over: Optional[EventType[[]]] = None,
on_mouse_up: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None,
on_paste: Optional[EventType[[]]] = None, on_paste: Optional[EventType] = None,
on_resize_editor: Optional[EventType[[]]] = None, on_resize_editor: Optional[EventType] = None,
on_scroll: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None,
on_unmount: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None,
toggle_code_view: Optional[EventType[[]]] = None, toggle_code_view: Optional[EventType] = None,
toggle_full_screen: Optional[EventType[[]]] = None, toggle_full_screen: Optional[EventType] = None,
**props, **props,
) -> "Editor": ) -> "Editor":
"""Create an instance of Editor. No children allowed. """Create an instance of Editor. No children allowed.

View File

@ -21,6 +21,7 @@ from typing import (
TypeVar, TypeVar,
Union, Union,
get_type_hints, get_type_hints,
overload,
) )
from typing_extensions import ParamSpec, get_args, get_origin from typing_extensions import ParamSpec, get_args, get_origin
@ -31,12 +32,15 @@ from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch
from reflex.utils.types import ArgsSpec, GenericType from reflex.utils.types import ArgsSpec, GenericType
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import ( from reflex.vars.base import (
CachedVarOperation,
LiteralVar, LiteralVar,
Var, Var,
cached_property_no_lock,
) )
from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar, FunctionVar from reflex.vars.function import (
ArgsFunctionOperation,
FunctionStringVar,
FunctionVar,
VarOperationCall,
)
from reflex.vars.object import ObjectVar from reflex.vars.object import ObjectVar
try: try:
@ -392,11 +396,6 @@ class EventChain(EventActionsMixin):
invocation: Optional[Var] = dataclasses.field(default=None) invocation: Optional[Var] = dataclasses.field(default=None)
# These chains can be used for their side effects when no other events are desired.
stop_propagation = EventChain(events=[], args_spec=lambda: []).stop_propagation
prevent_default = EventChain(events=[], args_spec=lambda: []).prevent_default
@dataclasses.dataclass( @dataclasses.dataclass(
init=True, init=True,
frozen=True, frozen=True,
@ -460,6 +459,11 @@ def empty_event() -> Tuple[()]:
return tuple() # type: ignore return tuple() # type: ignore
# These chains can be used for their side effects when no other events are desired.
stop_propagation = EventChain(events=[], args_spec=empty_event).stop_propagation
prevent_default = EventChain(events=[], args_spec=empty_event).prevent_default
T = TypeVar("T") T = TypeVar("T")
@ -1038,7 +1042,8 @@ def resolve_annotation(annotations: dict[str, Any], arg_name: str):
deprecation_version="0.6.3", deprecation_version="0.6.3",
removal_version="0.7.0", removal_version="0.7.0",
) )
return JavascriptInputEvent # Allow arbitrary attribute access two levels deep until removed.
return Dict[str, dict]
return annotation return annotation
@ -1255,7 +1260,7 @@ class EventVar(ObjectVar, python_types=EventSpec):
frozen=True, frozen=True,
**{"slots": True} if sys.version_info >= (3, 10) else {}, **{"slots": True} if sys.version_info >= (3, 10) else {},
) )
class LiteralEventVar(CachedVarOperation, LiteralVar, EventVar): class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
"""A literal event var.""" """A literal event var."""
_var_value: EventSpec = dataclasses.field(default=None) # type: ignore _var_value: EventSpec = dataclasses.field(default=None) # type: ignore
@ -1268,35 +1273,6 @@ class LiteralEventVar(CachedVarOperation, LiteralVar, EventVar):
""" """
return hash((self.__class__.__name__, self._js_expr)) return hash((self.__class__.__name__, self._js_expr))
@cached_property_no_lock
def _cached_var_name(self) -> str:
"""The name of the var.
Returns:
The name of the var.
"""
return str(
FunctionStringVar("Event").call(
# event handler name
".".join(
filter(
None,
format.get_event_handler_parts(self._var_value.handler),
)
),
# event handler args
{str(name): value for name, value in self._var_value.args},
# event actions
self._var_value.event_actions,
# client handler name
*(
[self._var_value.client_handler_name]
if self._var_value.client_handler_name
else []
),
)
)
@classmethod @classmethod
def create( def create(
cls, cls,
@ -1329,7 +1305,10 @@ class EventChainVar(FunctionVar, python_types=EventChain):
frozen=True, frozen=True,
**{"slots": True} if sys.version_info >= (3, 10) else {}, **{"slots": True} if sys.version_info >= (3, 10) else {},
) )
class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar): # Note: LiteralVar is second in the inheritance list allowing it act like a
# CachedVarOperation (ArgsFunctionOperation) and get the _js_expr from the
# _cached_var_name property.
class LiteralEventChainVar(ArgsFunctionOperation, LiteralVar, EventChainVar):
"""A literal event chain var.""" """A literal event chain var."""
_var_value: EventChain = dataclasses.field(default=None) # type: ignore _var_value: EventChain = dataclasses.field(default=None) # type: ignore
@ -1342,41 +1321,6 @@ class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar):
""" """
return hash((self.__class__.__name__, self._js_expr)) return hash((self.__class__.__name__, self._js_expr))
@cached_property_no_lock
def _cached_var_name(self) -> str:
"""The name of the var.
Returns:
The name of the var.
"""
sig = inspect.signature(self._var_value.args_spec) # type: ignore
if sig.parameters:
arg_def = tuple((f"_{p}" for p in sig.parameters))
arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def])
else:
# add a default argument for addEvents if none were specified in value.args_spec
# used to trigger the preventDefault() on the event.
arg_def = ("...args",)
arg_def_expr = Var(_js_expr="args")
if self._var_value.invocation is None:
invocation = FunctionStringVar.create("addEvents")
else:
invocation = self._var_value.invocation
return str(
ArgsFunctionOperation.create(
arg_def,
invocation.call(
LiteralVar.create(
[LiteralVar.create(event) for event in self._var_value.events]
),
arg_def_expr,
self._var_value.event_actions,
),
)
)
@classmethod @classmethod
def create( def create(
cls, cls,
@ -1392,10 +1336,31 @@ class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar):
Returns: Returns:
The created LiteralEventChainVar instance. The created LiteralEventChainVar instance.
""" """
sig = inspect.signature(value.args_spec) # type: ignore
if sig.parameters:
arg_def = tuple((f"_{p}" for p in sig.parameters))
arg_def_expr = LiteralVar.create([Var(_js_expr=arg) for arg in arg_def])
else:
# add a default argument for addEvents if none were specified in value.args_spec
# used to trigger the preventDefault() on the event.
arg_def = ("...args",)
arg_def_expr = Var(_js_expr="args")
if value.invocation is None:
invocation = FunctionStringVar.create("addEvents")
else:
invocation = value.invocation
return cls( return cls(
_js_expr="", _js_expr="",
_var_type=EventChain, _var_type=EventChain,
_var_data=_var_data, _var_data=_var_data,
_args_names=arg_def,
_return_expr=invocation.call(
LiteralVar.create([LiteralVar.create(event) for event in value.events]),
arg_def_expr,
value.event_actions,
),
_var_value=value, _var_value=value,
) )
@ -1408,6 +1373,11 @@ EventType = Union[IndividualEventType[G], List[IndividualEventType[G]]]
P = ParamSpec("P") P = ParamSpec("P")
T = TypeVar("T") T = TypeVar("T")
V = TypeVar("V")
V2 = TypeVar("V2")
V3 = TypeVar("V3")
V4 = TypeVar("V4")
V5 = TypeVar("V5")
if sys.version_info >= (3, 10): if sys.version_info >= (3, 10):
from typing import Concatenate from typing import Concatenate
@ -1423,7 +1393,55 @@ if sys.version_info >= (3, 10):
""" """
self.func = func self.func = func
def __get__(self, instance, owner) -> Callable[P, T]: @overload
def __get__(
self: EventCallback[[V], T], instance: None, owner
) -> Callable[[Union[Var[V], V]], EventSpec]: ...
@overload
def __get__(
self: EventCallback[[V, V2], T], instance: None, owner
) -> Callable[[Union[Var[V], V], Union[Var[V2], V2]], EventSpec]: ...
@overload
def __get__(
self: EventCallback[[V, V2, V3], T], instance: None, owner
) -> Callable[
[Union[Var[V], V], Union[Var[V2], V2], Union[Var[V3], V3]],
EventSpec,
]: ...
@overload
def __get__(
self: EventCallback[[V, V2, V3, V4], T], instance: None, owner
) -> Callable[
[
Union[Var[V], V],
Union[Var[V2], V2],
Union[Var[V3], V3],
Union[Var[V4], V4],
],
EventSpec,
]: ...
@overload
def __get__(
self: EventCallback[[V, V2, V3, V4, V5], T], instance: None, owner
) -> Callable[
[
Union[Var[V], V],
Union[Var[V2], V2],
Union[Var[V3], V3],
Union[Var[V4], V4],
Union[Var[V5], V5],
],
EventSpec,
]: ...
@overload
def __get__(self, instance, owner) -> Callable[P, T]: ...
def __get__(self, instance, owner) -> Callable:
"""Get the function with the instance bound to it. """Get the function with the instance bound to it.
Args: Args:

View File

@ -4,7 +4,6 @@ from __future__ import annotations
import atexit import atexit
import os import os
import webbrowser
from pathlib import Path from pathlib import Path
from typing import List, Optional from typing import List, Optional
@ -586,18 +585,6 @@ def deploy(
) )
@cli.command()
def demo(
frontend_port: str = typer.Option(
"3001", help="Specify a different frontend port."
),
backend_port: str = typer.Option("8001", help="Specify a different backend port."),
):
"""Run the demo app."""
# Open the demo app in a terminal.
webbrowser.open("https://demo.reflex.run")
cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.") cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.")
cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.") cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.")
cli.add_typer( cli.add_typer(

View File

@ -2566,9 +2566,9 @@ class StateManager(Base, ABC):
The state manager (either disk, memory or redis). The state manager (either disk, memory or redis).
""" """
config = get_config() config = get_config()
if config.state_manager_mode == constants.StateManagerMode.DISK:
return StateManagerMemory(state=state)
if config.state_manager_mode == constants.StateManagerMode.MEMORY: if config.state_manager_mode == constants.StateManagerMode.MEMORY:
return StateManagerMemory(state=state)
if config.state_manager_mode == constants.StateManagerMode.DISK:
return StateManagerDisk(state=state) return StateManagerDisk(state=state)
if config.state_manager_mode == constants.StateManagerMode.REDIS: if config.state_manager_mode == constants.StateManagerMode.REDIS:
redis = prerequisites.get_redis() redis = prerequisites.get_redis()

View File

@ -10,6 +10,7 @@ from reflex.event import EventChain, EventHandler
from reflex.utils import format from reflex.utils import format
from reflex.utils.exceptions import ReflexError from reflex.utils.exceptions import ReflexError
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.utils.types import get_origin
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import CallableVar, LiteralVar, Var from reflex.vars.base import CallableVar, LiteralVar, Var
from reflex.vars.function import FunctionVar from reflex.vars.function import FunctionVar
@ -196,6 +197,10 @@ def convert(
isinstance(value, Breakpoints) isinstance(value, Breakpoints)
and all(not isinstance(v, dict) for v in value.values()) and all(not isinstance(v, dict) for v in value.values())
) )
or (
isinstance(value, ObjectVar)
and not issubclass(get_origin(value._var_type) or value._var_type, dict)
)
else (key,) else (key,)
) )

View File

@ -185,7 +185,8 @@ def get_node_path() -> str | None:
""" """
node_path = Path(constants.Node.PATH) node_path = Path(constants.Node.PATH)
if use_system_node() or not node_path.exists(): if use_system_node() or not node_path.exists():
return str(which("node")) system_node_path = which("node")
return str(system_node_path) if system_node_path else None
return str(node_path) return str(node_path)
@ -197,7 +198,8 @@ def get_npm_path() -> str | None:
""" """
npm_path = Path(constants.Node.NPM_PATH) npm_path = Path(constants.Node.NPM_PATH)
if use_system_node() or not npm_path.exists(): if use_system_node() or not npm_path.exists():
return str(which("npm")) system_npm_path = which("npm")
return str(system_npm_path) if system_npm_path else None
return str(npm_path) return str(npm_path)

View File

@ -146,14 +146,9 @@ def check_node_version() -> bool:
Whether the version of Node.js is valid. Whether the version of Node.js is valid.
""" """
current_version = get_node_version() current_version = get_node_version()
if current_version: return current_version is not None and current_version >= version.parse(
# Compare the version numbers constants.Node.MIN_VERSION
return ( )
current_version >= version.parse(constants.Node.MIN_VERSION)
if constants.IS_WINDOWS or path_ops.use_system_node()
else current_version == version.parse(constants.Node.VERSION)
)
return False
def get_node_version() -> version.Version | None: def get_node_version() -> version.Version | None:

View File

@ -429,7 +429,7 @@ def _generate_component_create_functiondef(
def figure_out_return_type(annotation: Any): def figure_out_return_type(annotation: Any):
if inspect.isclass(annotation) and issubclass(annotation, inspect._empty): if inspect.isclass(annotation) and issubclass(annotation, inspect._empty):
return ast.Name(id="Optional[EventType[[]]]") return ast.Name(id="Optional[EventType]")
if isinstance(annotation, str) and annotation.startswith("Tuple["): if isinstance(annotation, str) and annotation.startswith("Tuple["):
inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]") inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]")

View File

@ -2,11 +2,18 @@ from typing import List
import pytest import pytest
from reflex import event from reflex.event import (
from reflex.event import Event, EventHandler, EventSpec, call_event_handler, fix_events Event,
EventChain,
EventHandler,
EventSpec,
call_event_handler,
event,
fix_events,
)
from reflex.state import BaseState from reflex.state import BaseState
from reflex.utils import format from reflex.utils import format
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import Field, LiteralVar, Var, field
def make_var(value) -> Var: def make_var(value) -> Var:
@ -388,3 +395,28 @@ def test_event_actions_on_state():
assert sp_handler.event_actions == {"stopPropagation": True} assert sp_handler.event_actions == {"stopPropagation": True}
# should NOT affect other references to the handler # should NOT affect other references to the handler
assert not handler.event_actions assert not handler.event_actions
def test_event_var_data():
class S(BaseState):
x: Field[int] = field(0)
@event
def s(self, value: int):
pass
# Handler doesn't have any _var_data because it's just a str
handler_var = Var.create(S.s)
assert handler_var._get_all_var_data() is None
# Ensure spec carries _var_data
spec_var = Var.create(S.s(S.x))
assert spec_var._get_all_var_data() == S.x._get_all_var_data()
# Needed to instantiate the EventChain
def _args_spec(value: Var[int]) -> tuple[Var[int]]:
return (value,)
# Ensure chain carries _var_data
chain_var = Var.create(EventChain(events=[S.s(S.x)], args_spec=_args_spec))
assert chain_var._get_all_var_data() == S.x._get_all_var_data()