Compare commits

...

14 Commits
main ... v0.6.3

Author SHA1 Message Date
Masen Furer
addf633692
pyproject.toml: bump to 0.6.3 2024-10-18 09:22:07 -07:00
Masen Furer
004518dd51
pyproject: bump to 0.6.3a4 2024-10-17 19:22:20 -07:00
Masen Furer
b112d35f62
Fix runtime error on python 3.11.0 (#4197)
All generic types present in a Union must be parametrized on 3.11.0 if any
other generic types in the union are parametrized.

This appears to be a bug in 3.11.0, as the behavior is not observed in 3.11.1
or 3.10; fixing here as this is technically more correct anyway and avoids a
crash.
2024-10-17 19:22:12 -07:00
Masen Furer
2aea1f29b6
pyproject: bump to 0.6.3a3 2024-10-17 16:58:16 -07:00
Masen Furer
0457cbd747
When REDIS_URL is set, use redis, regardless of config preference. (#4196)
We might change this down the road, but we don't want to introduce a breaking
change at this time.
2024-10-17 16:58:08 -07:00
Masen Furer
a6b2e640ac
[ENG-3954] Treat ArrayVar.foreach index as int (#4193)
* [ENG-3954] Treat ArrayVar.foreach index as int

* foreach: convert return value to a Var

When the value returned from the foreach is not hashable (mutable type), then
it will raise an exception if it is not first converted to a LiteralVar.
2024-10-17 16:58:07 -07:00
Masen Furer
542382c3e7
bump to 0.6.3a2 2024-10-16 15:15:08 -07:00
Khaleel Al-Adhami
20398c10f1
fix pyi for untyped event handlers (#4186)
* fix pyi for untyped event handlers

* no more empty lambdas
2024-10-16 15:14:58 -07:00
Masen Furer
f8881c391d
Arbitrary arg access two levels deep for untyped handler (#4180)
* Arbitrary arg access two levels deep for untyped handler

Provide drop-in compatibility with existing component wrapping code
that was accessing attributes on the default handler arg type.

* py3.9 compat
2024-10-16 15:14:58 -07:00
Masen Furer
c460040040
LiteralEventChainVar becomes an ArgsFunctionOperation (#4174)
* LiteralEventChainVar becomes an ArgsFunctionOperation

Instead of using the ArgsFunctionOperation to create the string representation
of the _js_expr, make the identity of the var an ArgsFunctionOperation so the
_args_names and _return_expr remain accessible.

Rely on the default behavior of ArgsFunctionOperation to create the
_cached_var_name / _js_expr value.

This allows the compat shim in `format_event_chain` to remain functional, as it
does special handling for ArgsFunctionOperation to retain the previous behavior
of that function (this was a regression introduced in 0.6.2).

* _var_type is EventChain; fix parent class order

* Re-fix LiteralEventChainVar inheritence list w/ comment

* [ENG-3942] LiteralEventVar becomes VarCallOperation

instead of using `.call` when constructing the `_js_expr`, have the identity of
a LiteralEventVar as a VarCallOperation to take advantage of the _var_data
carrying.

* add event overlords

* EventCallback descriptor always returns EventSpec from class

Relax actual `__get__` definition to support the multitude of overloads

* test case for event related vars carrying _var_data

---------

Co-authored-by: Khaleel Al-Adhami <khaleel.aladhami@gmail.com>
2024-10-16 15:14:58 -07:00
Khaleel Al-Adhami
19bfdc4035
disk is memory is disk (#4185) 2024-10-16 15:14:58 -07:00
Khaleel Al-Adhami
f133bf53cc
only treat dict object vars as key value mapping (#4177) 2024-10-16 15:14:58 -07:00
Masen Furer
11dcce3975
pin AppHarness tests to ubuntu-22.04 runner (#4173) 2024-10-16 15:14:57 -07:00
Masen Furer
3494a2d3f3
bump to 0.6.3a1 2024-10-14 08:57:37 -07:00
19 changed files with 243 additions and 166 deletions

View File

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

View File

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

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "reflex"
version = "0.6.3dev1"
version = "0.6.3"
description = "Web apps in pure Python."
license = "Apache-2.0"
authors = [
@ -103,4 +103,4 @@ lint.pydocstyle.convention = "google"
[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function"
asyncio_mode = "auto"
asyncio_mode = "auto"

View File

@ -31,7 +31,7 @@ class ErrorBoundary(Component):
on_click: Optional[EventType[[]]] = None,
on_context_menu: 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_mount: Optional[EventType[[]]] = None,
on_mouse_down: Optional[EventType[[]]] = None,

View File

@ -45,6 +45,7 @@ from reflex.event import (
EventVar,
call_event_fn,
call_event_handler,
empty_event,
get_handler_args,
)
from reflex.style import Style, format_as_emotion
@ -623,21 +624,21 @@ class Component(BaseComponent, ABC):
"""
default_triggers = {
EventTriggers.ON_FOCUS: lambda: [],
EventTriggers.ON_BLUR: lambda: [],
EventTriggers.ON_CLICK: lambda: [],
EventTriggers.ON_CONTEXT_MENU: lambda: [],
EventTriggers.ON_DOUBLE_CLICK: lambda: [],
EventTriggers.ON_MOUSE_DOWN: lambda: [],
EventTriggers.ON_MOUSE_ENTER: lambda: [],
EventTriggers.ON_MOUSE_LEAVE: lambda: [],
EventTriggers.ON_MOUSE_MOVE: lambda: [],
EventTriggers.ON_MOUSE_OUT: lambda: [],
EventTriggers.ON_MOUSE_OVER: lambda: [],
EventTriggers.ON_MOUSE_UP: lambda: [],
EventTriggers.ON_SCROLL: lambda: [],
EventTriggers.ON_MOUNT: lambda: [],
EventTriggers.ON_UNMOUNT: lambda: [],
EventTriggers.ON_FOCUS: empty_event,
EventTriggers.ON_BLUR: empty_event,
EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_CONTEXT_MENU: empty_event,
EventTriggers.ON_DOUBLE_CLICK: empty_event,
EventTriggers.ON_MOUSE_DOWN: empty_event,
EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: empty_event,
EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_UP: empty_event,
EventTriggers.ON_SCROLL: empty_event,
EventTriggers.ON_MOUNT: empty_event,
EventTriggers.ON_UNMOUNT: empty_event,
}
# Look for component specific triggers,
@ -648,7 +649,7 @@ class Component(BaseComponent, ABC):
annotation = field.annotation
if (metadata := getattr(annotation, "__metadata__", None)) is not None:
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
def __repr__(self) -> str:
@ -1705,7 +1706,7 @@ class CustomComponent(Component):
value = self._create_event_chain(
value=value,
args_spec=event_triggers_in_component_declaration.get(
key, lambda: []
key, empty_event
),
)
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_clicked: 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_column_resize: Optional[EventType[[]]] = None,
on_column_resize: 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_finished_editing: Optional[EventType[[]]] = None,
on_finished_editing: Optional[EventType] = None,
on_focus: Optional[EventType[[]]] = None,
on_group_header_clicked: Optional[EventType[[]]] = None,
on_group_header_context_menu: Optional[EventType[[]]] = None,
on_group_header_renamed: Optional[EventType[[]]] = None,
on_group_header_clicked: Optional[EventType] = None,
on_group_header_context_menu: Optional[EventType] = None,
on_group_header_renamed: Optional[EventType] = None,
on_header_clicked: 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_mount: Optional[EventType[[]]] = None,
on_mouse_down: Optional[EventType[[]]] = None,

View File

@ -58,7 +58,7 @@ class Audio(ReactPlayer):
on_play: Optional[EventType[[]]] = None,
on_playback_quality_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_scroll: Optional[EventType[[]]] = None,
on_seek: Optional[EventType] = None,

View File

@ -56,7 +56,7 @@ class ReactPlayer(NoSSRComponent):
on_play: Optional[EventType[[]]] = None,
on_playback_quality_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_scroll: Optional[EventType[[]]] = None,
on_seek: Optional[EventType] = None,

View File

@ -58,7 +58,7 @@ class Video(ReactPlayer):
on_play: Optional[EventType[[]]] = None,
on_playback_quality_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_scroll: 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.
"""
return {
EventTriggers.ON_CHANGE: lambda: [],
EventTriggers.ON_CHANGE: empty_event,
}
@ -293,10 +293,10 @@ class Cartesian(Recharts):
name: Var[Union[str, int]]
# 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
on_animation_end: EventHandler[lambda: []]
on_animation_end: EventHandler[empty_event]
# The customized event handler of click on the component in this group
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.
"""
return {
EventTriggers.ON_CLICK: lambda: [],
EventTriggers.ON_MOUSE_ENTER: lambda: [],
EventTriggers.ON_MOUSE_LEAVE: lambda: [],
EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_ENTER: empty_event,
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.
"""
return {
EventTriggers.ON_CLICK: lambda: [],
EventTriggers.ON_MOUSE_DOWN: lambda: [],
EventTriggers.ON_MOUSE_UP: lambda: [],
EventTriggers.ON_MOUSE_MOVE: lambda: [],
EventTriggers.ON_MOUSE_OVER: lambda: [],
EventTriggers.ON_MOUSE_OUT: lambda: [],
EventTriggers.ON_MOUSE_ENTER: lambda: [],
EventTriggers.ON_MOUSE_LEAVE: lambda: [],
EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_DOWN: empty_event,
EventTriggers.ON_MOUSE_UP: empty_event,
EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_ENTER: empty_event,
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.
"""
return {
EventTriggers.ON_ANIMATION_START: lambda: [],
EventTriggers.ON_ANIMATION_END: lambda: [],
EventTriggers.ON_CLICK: lambda: [],
EventTriggers.ON_MOUSE_MOVE: lambda: [],
EventTriggers.ON_MOUSE_OVER: lambda: [],
EventTriggers.ON_MOUSE_OUT: lambda: [],
EventTriggers.ON_MOUSE_ENTER: lambda: [],
EventTriggers.ON_MOUSE_LEAVE: lambda: [],
EventTriggers.ON_ANIMATION_START: empty_event,
EventTriggers.ON_ANIMATION_END: empty_event,
EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_ENTER: empty_event,
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.
"""
return {
EventTriggers.ON_ANIMATION_START: lambda: [],
EventTriggers.ON_ANIMATION_END: lambda: [],
EventTriggers.ON_ANIMATION_START: empty_event,
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.
"""
return {
EventTriggers.ON_CLICK: lambda: [],
EventTriggers.ON_MOUSE_MOVE: lambda: [],
EventTriggers.ON_MOUSE_OVER: lambda: [],
EventTriggers.ON_MOUSE_OUT: lambda: [],
EventTriggers.ON_MOUSE_ENTER: lambda: [],
EventTriggers.ON_MOUSE_LEAVE: lambda: [],
EventTriggers.ON_ANIMATION_START: lambda: [],
EventTriggers.ON_ANIMATION_END: lambda: [],
EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: empty_event,
EventTriggers.ON_ANIMATION_START: empty_event,
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.
"""
return {
EventTriggers.ON_CLICK: lambda: [],
EventTriggers.ON_MOUSE_MOVE: lambda: [],
EventTriggers.ON_MOUSE_OVER: lambda: [],
EventTriggers.ON_MOUSE_OUT: lambda: [],
EventTriggers.ON_MOUSE_ENTER: lambda: [],
EventTriggers.ON_MOUSE_LEAVE: lambda: [],
EventTriggers.ON_CLICK: empty_event,
EventTriggers.ON_MOUSE_MOVE: empty_event,
EventTriggers.ON_MOUSE_OVER: empty_event,
EventTriggers.ON_MOUSE_OUT: empty_event,
EventTriggers.ON_MOUSE_ENTER: empty_event,
EventTriggers.ON_MOUSE_LEAVE: empty_event,
}

View File

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

View File

@ -22,6 +22,7 @@ from typing import (
TypeVar,
Union,
get_type_hints,
overload,
)
from typing_extensions import ParamSpec, get_args, get_origin
@ -32,14 +33,17 @@ from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch
from reflex.utils.types import ArgsSpec, GenericType
from reflex.vars import VarData
from reflex.vars.base import (
CachedVarOperation,
LiteralNoneVar,
LiteralVar,
ToOperation,
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
try:
@ -395,11 +399,6 @@ class EventChain(EventActionsMixin):
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(
init=True,
frozen=True,
@ -463,6 +462,11 @@ def empty_event() -> Tuple[()]:
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")
@ -1041,7 +1045,8 @@ def resolve_annotation(annotations: dict[str, Any], arg_name: str):
deprecation_version="0.6.3",
removal_version="0.7.0",
)
return JavascriptInputEvent
# Allow arbitrary attribute access two levels deep until removed.
return Dict[str, dict]
return annotation
@ -1258,7 +1263,7 @@ class EventVar(ObjectVar):
frozen=True,
**{"slots": True} if sys.version_info >= (3, 10) else {},
)
class LiteralEventVar(CachedVarOperation, LiteralVar, EventVar):
class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
"""A literal event var."""
_var_value: EventSpec = dataclasses.field(default=None) # type: ignore
@ -1271,35 +1276,6 @@ class LiteralEventVar(CachedVarOperation, LiteralVar, EventVar):
"""
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
def create(
cls,
@ -1320,6 +1296,22 @@ class LiteralEventVar(CachedVarOperation, LiteralVar, EventVar):
_var_type=EventSpec,
_var_data=_var_data,
_var_value=value,
_func=FunctionStringVar("Event"),
_args=(
# event handler name
".".join(
filter(
None,
format.get_event_handler_parts(value.handler),
)
),
# event handler args
{str(name): value for name, value in value.args},
# event actions
value.event_actions,
# client handler name
*([value.client_handler_name] if value.client_handler_name else []),
),
)
@ -1332,7 +1324,10 @@ class EventChainVar(FunctionVar):
frozen=True,
**{"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."""
_var_value: EventChain = dataclasses.field(default=None) # type: ignore
@ -1345,41 +1340,6 @@ class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar):
"""
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
def create(
cls,
@ -1395,10 +1355,31 @@ class LiteralEventChainVar(CachedVarOperation, LiteralVar, EventChainVar):
Returns:
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(
_js_expr="",
_var_type=EventChain,
_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,
)
@ -1431,12 +1412,17 @@ class ToEventChainVarOperation(ToOperation, EventChainVar):
G = ParamSpec("G")
IndividualEventType = Union[EventSpec, EventHandler, Callable[G, Any], Var]
IndividualEventType = Union[EventSpec, EventHandler, Callable[G, Any], Var[Any]]
EventType = Union[IndividualEventType[G], List[IndividualEventType[G]]]
P = ParamSpec("P")
T = TypeVar("T")
V = TypeVar("V")
V2 = TypeVar("V2")
V3 = TypeVar("V3")
V4 = TypeVar("V4")
V5 = TypeVar("V5")
if sys.version_info >= (3, 10):
from typing import Concatenate
@ -1452,7 +1438,55 @@ if sys.version_info >= (3, 10):
"""
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.
Args:

View File

@ -2566,9 +2566,11 @@ class StateManager(Base, ABC):
The state manager (either disk, memory or redis).
"""
config = get_config()
if config.state_manager_mode == constants.StateManagerMode.DISK:
return StateManagerMemory(state=state)
if prerequisites.parse_redis_url() is not None:
config.state_manager_mode = constants.StateManagerMode.REDIS
if config.state_manager_mode == constants.StateManagerMode.MEMORY:
return StateManagerMemory(state=state)
if config.state_manager_mode == constants.StateManagerMode.DISK:
return StateManagerDisk(state=state)
if config.state_manager_mode == constants.StateManagerMode.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.exceptions import ReflexError
from reflex.utils.imports import ImportVar
from reflex.utils.types import get_origin
from reflex.vars import VarData
from reflex.vars.base import CallableVar, LiteralVar, Var
from reflex.vars.function import FunctionVar
@ -196,6 +197,10 @@ def convert(
isinstance(value, Breakpoints)
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,)
)

View File

@ -429,7 +429,7 @@ def _generate_component_create_functiondef(
def figure_out_return_type(annotation: Any):
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["):
inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]")

View File

@ -1155,7 +1155,7 @@ class ArrayVar(Var[ARRAY_VAR_TYPE]):
function_var = ArgsFunctionOperation.create(tuple(), return_value)
else:
# generic number var
number_var = Var("").to(NumberVar)
number_var = Var("").to(NumberVar, int)
first_arg_type = self[number_var]._var_type
@ -1167,7 +1167,10 @@ class ArrayVar(Var[ARRAY_VAR_TYPE]):
_var_type=first_arg_type,
).guess_type()
function_var = ArgsFunctionOperation.create((arg_name,), fn(first_arg))
function_var = ArgsFunctionOperation.create(
(arg_name,),
Var.create(fn(first_arg)),
)
return map_array_operation(self, function_var)

View File

@ -2,11 +2,18 @@ from typing import List
import pytest
from reflex import event
from reflex.event import Event, EventHandler, EventSpec, call_event_handler, fix_events
from reflex.event import (
Event,
EventChain,
EventHandler,
EventSpec,
call_event_handler,
event,
fix_events,
)
from reflex.state import BaseState
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:
@ -388,3 +395,28 @@ def test_event_actions_on_state():
assert sp_handler.event_actions == {"stopPropagation": True}
# should NOT affect other references to the handler
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()