reflex 0.5.10a3__py3-none-any.whl → 0.6.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -4
- reflex/.templates/jinja/web/utils/context.js.jinja2 +1 -1
- reflex/.templates/jinja/web/utils/theme.js.jinja2 +1 -1
- reflex/__init__.py +3 -2
- reflex/__init__.pyi +2 -2
- reflex/app.py +43 -9
- reflex/base.py +3 -2
- reflex/compiler/compiler.py +6 -6
- reflex/compiler/utils.py +5 -3
- reflex/components/base/app_wrap.py +2 -4
- reflex/components/base/app_wrap.pyi +17 -17
- reflex/components/base/bare.py +7 -4
- reflex/components/base/body.pyi +17 -17
- reflex/components/base/document.pyi +81 -81
- reflex/components/base/error_boundary.py +10 -8
- reflex/components/base/error_boundary.pyi +20 -19
- reflex/components/base/fragment.pyi +17 -17
- reflex/components/base/head.pyi +33 -33
- reflex/components/base/link.pyi +34 -33
- reflex/components/base/meta.pyi +65 -65
- reflex/components/base/script.py +2 -1
- reflex/components/base/script.pyi +21 -20
- reflex/components/component.py +116 -145
- reflex/components/core/banner.py +59 -60
- reflex/components/core/banner.pyi +86 -150
- reflex/components/core/client_side_routing.py +2 -1
- reflex/components/core/client_side_routing.pyi +34 -33
- reflex/components/core/clipboard.py +2 -2
- reflex/components/core/clipboard.pyi +19 -18
- reflex/components/core/cond.py +21 -44
- reflex/components/core/debounce.py +6 -8
- reflex/components/core/debounce.pyi +19 -18
- reflex/components/core/foreach.py +5 -14
- reflex/components/core/html.pyi +18 -17
- reflex/components/core/match.py +36 -43
- reflex/components/core/upload.py +32 -25
- reflex/components/core/upload.pyi +84 -73
- reflex/components/datadisplay/code.py +55 -28
- reflex/components/datadisplay/code.pyi +20 -17
- reflex/components/datadisplay/dataeditor.py +17 -11
- reflex/components/datadisplay/dataeditor.pyi +34 -33
- reflex/components/el/__init__.py +0 -1
- reflex/components/el/__init__.pyi +0 -11
- reflex/components/el/element.pyi +17 -17
- reflex/components/el/elements/__init__.py +1 -7
- reflex/components/el/elements/__init__.pyi +1 -15
- reflex/components/el/elements/base.pyi +18 -17
- reflex/components/el/elements/forms.py +24 -31
- reflex/components/el/elements/forms.pyi +237 -236
- reflex/components/el/elements/inline.pyi +450 -449
- reflex/components/el/elements/media.py +0 -21
- reflex/components/el/elements/media.pyi +338 -337
- reflex/components/el/elements/metadata.py +3 -2
- reflex/components/el/elements/metadata.pyi +98 -97
- reflex/components/el/elements/other.pyi +114 -113
- reflex/components/el/elements/scripts.pyi +50 -49
- reflex/components/el/elements/sectioning.pyi +242 -241
- reflex/components/el/elements/tables.pyi +162 -161
- reflex/components/el/elements/typography.pyi +242 -241
- reflex/components/gridjs/datatable.py +13 -14
- reflex/components/gridjs/datatable.pyi +34 -33
- reflex/components/lucide/icon.py +2 -126
- reflex/components/lucide/icon.pyi +34 -142
- reflex/components/markdown/markdown.py +30 -35
- reflex/components/markdown/markdown.pyi +29 -32
- reflex/components/moment/moment.pyi +19 -18
- reflex/components/next/base.pyi +17 -17
- reflex/components/next/image.py +0 -4
- reflex/components/next/image.pyi +20 -19
- reflex/components/next/link.pyi +18 -17
- reflex/components/next/video.pyi +18 -17
- reflex/components/plotly/plotly.py +16 -28
- reflex/components/plotly/plotly.pyi +36 -35
- reflex/components/props.py +21 -10
- reflex/components/radix/__init__.pyi +1 -1
- reflex/components/radix/primitives/__init__.pyi +0 -1
- reflex/components/radix/primitives/accordion.py +7 -8
- reflex/components/radix/primitives/accordion.pyi +117 -116
- reflex/components/radix/primitives/base.pyi +34 -33
- reflex/components/radix/primitives/drawer.pyi +169 -168
- reflex/components/radix/primitives/form.pyi +168 -167
- reflex/components/radix/primitives/progress.pyi +82 -81
- reflex/components/radix/primitives/slider.pyi +84 -83
- reflex/components/radix/themes/base.py +8 -4
- reflex/components/radix/themes/base.pyi +114 -113
- reflex/components/radix/themes/color_mode.py +12 -21
- reflex/components/radix/themes/color_mode.pyi +67 -67
- reflex/components/radix/themes/components/__init__.pyi +1 -0
- reflex/components/radix/themes/components/alert_dialog.pyi +118 -117
- reflex/components/radix/themes/components/aspect_ratio.pyi +18 -17
- reflex/components/radix/themes/components/avatar.pyi +18 -17
- reflex/components/radix/themes/components/badge.pyi +18 -17
- reflex/components/radix/themes/components/button.pyi +18 -17
- reflex/components/radix/themes/components/callout.pyi +82 -81
- reflex/components/radix/themes/components/card.pyi +18 -17
- reflex/components/radix/themes/components/checkbox.py +2 -3
- reflex/components/radix/themes/components/checkbox.pyi +53 -52
- reflex/components/radix/themes/components/checkbox_cards.pyi +34 -33
- reflex/components/radix/themes/components/checkbox_group.pyi +34 -33
- reflex/components/radix/themes/components/context_menu.pyi +140 -139
- reflex/components/radix/themes/components/data_list.py +5 -0
- reflex/components/radix/themes/components/data_list.pyi +71 -65
- reflex/components/radix/themes/components/dialog.pyi +121 -120
- reflex/components/radix/themes/components/dropdown_menu.pyi +142 -141
- reflex/components/radix/themes/components/hover_card.pyi +68 -67
- reflex/components/radix/themes/components/icon_button.py +2 -1
- reflex/components/radix/themes/components/icon_button.pyi +18 -17
- reflex/components/radix/themes/components/inset.pyi +18 -17
- reflex/components/radix/themes/components/popover.pyi +73 -72
- reflex/components/radix/themes/components/progress.pyi +18 -17
- reflex/components/radix/themes/components/radio.pyi +18 -17
- reflex/components/radix/themes/components/radio_cards.pyi +35 -34
- reflex/components/radix/themes/components/radio_group.py +35 -31
- reflex/components/radix/themes/components/radio_group.pyi +73 -66
- reflex/components/radix/themes/components/scroll_area.pyi +18 -17
- reflex/components/radix/themes/components/segmented_control.pyi +35 -34
- reflex/components/radix/themes/components/select.py +2 -1
- reflex/components/radix/themes/components/select.pyi +155 -154
- reflex/components/radix/themes/components/separator.py +2 -3
- reflex/components/radix/themes/components/separator.pyi +18 -17
- reflex/components/radix/themes/components/skeleton.pyi +18 -17
- reflex/components/radix/themes/components/slider.py +2 -1
- reflex/components/radix/themes/components/slider.pyi +20 -19
- reflex/components/radix/themes/components/spinner.pyi +18 -17
- reflex/components/radix/themes/components/switch.pyi +19 -18
- reflex/components/radix/themes/components/table.pyi +114 -113
- reflex/components/radix/themes/components/tabs.pyi +84 -83
- reflex/components/radix/themes/components/text_area.pyi +21 -20
- reflex/components/radix/themes/components/text_field.py +0 -79
- reflex/components/radix/themes/components/text_field.pyi +57 -63
- reflex/components/radix/themes/components/tooltip.pyi +21 -20
- reflex/components/radix/themes/layout/base.pyi +18 -17
- reflex/components/radix/themes/layout/box.pyi +18 -17
- reflex/components/radix/themes/layout/center.pyi +18 -17
- reflex/components/radix/themes/layout/container.py +2 -3
- reflex/components/radix/themes/layout/container.pyi +18 -17
- reflex/components/radix/themes/layout/flex.pyi +18 -17
- reflex/components/radix/themes/layout/grid.pyi +18 -17
- reflex/components/radix/themes/layout/list.py +5 -4
- reflex/components/radix/themes/layout/list.pyi +86 -85
- reflex/components/radix/themes/layout/section.py +2 -3
- reflex/components/radix/themes/layout/section.pyi +18 -17
- reflex/components/radix/themes/layout/spacer.pyi +18 -17
- reflex/components/radix/themes/layout/stack.pyi +50 -49
- reflex/components/radix/themes/typography/blockquote.pyi +18 -17
- reflex/components/radix/themes/typography/code.pyi +18 -17
- reflex/components/radix/themes/typography/heading.pyi +18 -17
- reflex/components/radix/themes/typography/link.pyi +18 -17
- reflex/components/radix/themes/typography/text.pyi +114 -113
- reflex/components/react_player/audio.pyi +34 -33
- reflex/components/react_player/react_player.pyi +34 -33
- reflex/components/react_player/video.pyi +34 -33
- reflex/components/recharts/cartesian.py +23 -19
- reflex/components/recharts/cartesian.pyi +297 -296
- reflex/components/recharts/charts.py +6 -5
- reflex/components/recharts/charts.pyi +179 -178
- reflex/components/recharts/general.py +8 -7
- reflex/components/recharts/general.pyi +82 -81
- reflex/components/recharts/polar.py +14 -13
- reflex/components/recharts/polar.pyi +76 -75
- reflex/components/recharts/recharts.pyi +33 -33
- reflex/components/sonner/toast.py +30 -33
- reflex/components/sonner/toast.pyi +27 -25
- reflex/components/suneditor/editor.py +2 -1
- reflex/components/suneditor/editor.pyi +27 -26
- reflex/components/tags/iter_tag.py +16 -16
- reflex/components/tags/tag.py +8 -10
- reflex/constants/base.py +3 -1
- reflex/constants/event.py +1 -0
- reflex/event.py +89 -79
- reflex/experimental/__init__.py +25 -6
- reflex/experimental/client_state.py +34 -58
- reflex/experimental/hooks.py +13 -18
- reflex/experimental/layout.py +5 -5
- reflex/experimental/layout.pyi +84 -83
- reflex/{experimental/vars → ivars}/__init__.py +0 -1
- reflex/ivars/base.py +2180 -0
- reflex/ivars/function.py +200 -0
- reflex/ivars/number.py +1137 -0
- reflex/ivars/object.py +564 -0
- reflex/ivars/sequence.py +1601 -0
- reflex/model.py +22 -0
- reflex/reflex.py +4 -0
- reflex/state.py +388 -73
- reflex/style.py +52 -34
- reflex/testing.py +8 -3
- reflex/utils/exceptions.py +12 -0
- reflex/utils/exec.py +0 -14
- reflex/utils/format.py +74 -223
- reflex/utils/net.py +43 -0
- reflex/utils/path_ops.py +13 -1
- reflex/utils/prerequisites.py +46 -26
- reflex/utils/pyi_generator.py +5 -4
- reflex/utils/serializers.py +13 -31
- reflex/utils/types.py +44 -9
- reflex/vars.py +127 -2230
- {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/METADATA +4 -6
- reflex-0.6.0a1.dist-info/RECORD +384 -0
- reflex/.templates/apps/demo/.gitignore +0 -4
- reflex/.templates/apps/demo/assets/favicon.ico +0 -0
- reflex/.templates/apps/demo/assets/github.svg +0 -10
- reflex/.templates/apps/demo/assets/icon.svg +0 -37
- reflex/.templates/apps/demo/assets/logo.svg +0 -68
- reflex/.templates/apps/demo/assets/paneleft.svg +0 -13
- reflex/.templates/apps/demo/code/__init__.py +0 -1
- reflex/.templates/apps/demo/code/demo.py +0 -127
- reflex/.templates/apps/demo/code/pages/__init__.py +0 -7
- reflex/.templates/apps/demo/code/pages/chatapp.py +0 -31
- reflex/.templates/apps/demo/code/pages/datatable.py +0 -360
- reflex/.templates/apps/demo/code/pages/forms.py +0 -257
- reflex/.templates/apps/demo/code/pages/graphing.py +0 -253
- reflex/.templates/apps/demo/code/pages/home.py +0 -56
- reflex/.templates/apps/demo/code/sidebar.py +0 -178
- reflex/.templates/apps/demo/code/state.py +0 -22
- reflex/.templates/apps/demo/code/states/form_state.py +0 -40
- reflex/.templates/apps/demo/code/states/pie_state.py +0 -47
- reflex/.templates/apps/demo/code/styles.py +0 -68
- reflex/.templates/apps/demo/code/webui/__init__.py +0 -0
- reflex/.templates/apps/demo/code/webui/components/__init__.py +0 -4
- reflex/.templates/apps/demo/code/webui/components/chat.py +0 -118
- reflex/.templates/apps/demo/code/webui/components/loading_icon.py +0 -19
- reflex/.templates/apps/demo/code/webui/components/modal.py +0 -56
- reflex/.templates/apps/demo/code/webui/components/navbar.py +0 -70
- reflex/.templates/apps/demo/code/webui/components/sidebar.py +0 -66
- reflex/.templates/apps/demo/code/webui/state.py +0 -146
- reflex/.templates/apps/demo/code/webui/styles.py +0 -88
- reflex/experimental/vars/base.py +0 -583
- reflex/experimental/vars/function.py +0 -290
- reflex/experimental/vars/number.py +0 -1458
- reflex/experimental/vars/object.py +0 -804
- reflex/experimental/vars/sequence.py +0 -1764
- reflex/utils/watch.py +0 -96
- reflex/vars.pyi +0 -218
- reflex-0.5.10a3.dist-info/RECORD +0 -413
- {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/LICENSE +0 -0
- {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/WHEEL +0 -0
- {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/entry_points.txt +0 -0
reflex/event.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
|
+
import types
|
|
6
7
|
import urllib.parse
|
|
7
8
|
from base64 import b64encode
|
|
8
9
|
from typing import (
|
|
@@ -18,9 +19,13 @@ from typing import (
|
|
|
18
19
|
|
|
19
20
|
from reflex import constants
|
|
20
21
|
from reflex.base import Base
|
|
22
|
+
from reflex.ivars.base import ImmutableVar, LiteralVar
|
|
23
|
+
from reflex.ivars.function import FunctionStringVar, FunctionVar
|
|
24
|
+
from reflex.ivars.object import ObjectVar
|
|
21
25
|
from reflex.utils import format
|
|
26
|
+
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch
|
|
22
27
|
from reflex.utils.types import ArgsSpec
|
|
23
|
-
from reflex.vars import
|
|
28
|
+
from reflex.vars import Var, VarData
|
|
24
29
|
|
|
25
30
|
try:
|
|
26
31
|
from typing import Annotated
|
|
@@ -186,7 +191,7 @@ class EventHandler(EventActionsMixin):
|
|
|
186
191
|
|
|
187
192
|
# Get the function args.
|
|
188
193
|
fn_args = inspect.getfullargspec(self.fn).args[1:]
|
|
189
|
-
fn_args = (
|
|
194
|
+
fn_args = (ImmutableVar.create_safe(arg) for arg in fn_args)
|
|
190
195
|
|
|
191
196
|
# Construct the payload.
|
|
192
197
|
values = []
|
|
@@ -197,7 +202,7 @@ class EventHandler(EventActionsMixin):
|
|
|
197
202
|
|
|
198
203
|
# Otherwise, convert to JSON.
|
|
199
204
|
try:
|
|
200
|
-
values.append(
|
|
205
|
+
values.append(LiteralVar.create(arg))
|
|
201
206
|
except TypeError as e:
|
|
202
207
|
raise EventHandlerTypeError(
|
|
203
208
|
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
|
@@ -224,7 +229,7 @@ class EventSpec(EventActionsMixin):
|
|
|
224
229
|
client_handler_name: str = ""
|
|
225
230
|
|
|
226
231
|
# The arguments to pass to the function.
|
|
227
|
-
args: Tuple[Tuple[
|
|
232
|
+
args: Tuple[Tuple[ImmutableVar, ImmutableVar], ...] = ()
|
|
228
233
|
|
|
229
234
|
class Config:
|
|
230
235
|
"""The Pydantic config."""
|
|
@@ -232,7 +237,9 @@ class EventSpec(EventActionsMixin):
|
|
|
232
237
|
# Required to allow tuple fields.
|
|
233
238
|
frozen = True
|
|
234
239
|
|
|
235
|
-
def with_args(
|
|
240
|
+
def with_args(
|
|
241
|
+
self, args: Tuple[Tuple[ImmutableVar, ImmutableVar], ...]
|
|
242
|
+
) -> EventSpec:
|
|
236
243
|
"""Copy the event spec, with updated args.
|
|
237
244
|
|
|
238
245
|
Args:
|
|
@@ -248,7 +255,7 @@ class EventSpec(EventActionsMixin):
|
|
|
248
255
|
event_actions=self.event_actions.copy(),
|
|
249
256
|
)
|
|
250
257
|
|
|
251
|
-
def add_args(self, *args:
|
|
258
|
+
def add_args(self, *args: ImmutableVar) -> EventSpec:
|
|
252
259
|
"""Add arguments to the event spec.
|
|
253
260
|
|
|
254
261
|
Args:
|
|
@@ -264,13 +271,13 @@ class EventSpec(EventActionsMixin):
|
|
|
264
271
|
|
|
265
272
|
# Get the remaining unfilled function args.
|
|
266
273
|
fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
|
|
267
|
-
fn_args = (
|
|
274
|
+
fn_args = (ImmutableVar.create_safe(arg) for arg in fn_args)
|
|
268
275
|
|
|
269
276
|
# Construct the payload.
|
|
270
277
|
values = []
|
|
271
278
|
for arg in args:
|
|
272
279
|
try:
|
|
273
|
-
values.append(
|
|
280
|
+
values.append(LiteralVar.create(arg))
|
|
274
281
|
except TypeError as e:
|
|
275
282
|
raise EventHandlerTypeError(
|
|
276
283
|
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
|
|
@@ -388,15 +395,16 @@ class FileUpload(Base):
|
|
|
388
395
|
upload_id = self.upload_id or DEFAULT_UPLOAD_ID
|
|
389
396
|
spec_args = [
|
|
390
397
|
(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
398
|
+
ImmutableVar.create_safe("files"),
|
|
399
|
+
ImmutableVar(
|
|
400
|
+
_var_name="filesById",
|
|
401
|
+
_var_type=Dict[str, Any],
|
|
402
|
+
_var_data=VarData.merge(upload_files_context_var_data),
|
|
403
|
+
).to(ObjectVar)[LiteralVar.create(upload_id)],
|
|
396
404
|
),
|
|
397
405
|
(
|
|
398
|
-
|
|
399
|
-
|
|
406
|
+
ImmutableVar.create_safe("upload_id"),
|
|
407
|
+
LiteralVar.create(upload_id),
|
|
400
408
|
),
|
|
401
409
|
]
|
|
402
410
|
if self.on_upload_progress is not None:
|
|
@@ -415,7 +423,7 @@ class FileUpload(Base):
|
|
|
415
423
|
) # type: ignore
|
|
416
424
|
else:
|
|
417
425
|
raise ValueError(f"{on_upload_progress} is not a valid event handler.")
|
|
418
|
-
if isinstance(events,
|
|
426
|
+
if isinstance(events, ImmutableVar):
|
|
419
427
|
raise ValueError(f"{on_upload_progress} cannot return a var {events}.")
|
|
420
428
|
on_upload_progress_chain = EventChain(
|
|
421
429
|
events=events,
|
|
@@ -424,11 +432,10 @@ class FileUpload(Base):
|
|
|
424
432
|
formatted_chain = str(format.format_prop(on_upload_progress_chain))
|
|
425
433
|
spec_args.append(
|
|
426
434
|
(
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
),
|
|
435
|
+
ImmutableVar.create_safe("on_upload_progress"),
|
|
436
|
+
FunctionStringVar(
|
|
437
|
+
formatted_chain.strip("{}"),
|
|
438
|
+
).to(FunctionVar, EventChain),
|
|
432
439
|
),
|
|
433
440
|
)
|
|
434
441
|
return EventSpec(
|
|
@@ -465,8 +472,8 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
|
|
|
465
472
|
handler=EventHandler(fn=fn),
|
|
466
473
|
args=tuple(
|
|
467
474
|
(
|
|
468
|
-
|
|
469
|
-
|
|
475
|
+
ImmutableVar.create_safe(k),
|
|
476
|
+
LiteralVar.create(v),
|
|
470
477
|
)
|
|
471
478
|
for k, v in kwargs.items()
|
|
472
479
|
),
|
|
@@ -542,7 +549,7 @@ def set_focus(ref: str) -> EventSpec:
|
|
|
542
549
|
return server_side(
|
|
543
550
|
"_set_focus",
|
|
544
551
|
get_fn_signature(set_focus),
|
|
545
|
-
ref=
|
|
552
|
+
ref=LiteralVar.create(format.format_ref(ref)),
|
|
546
553
|
)
|
|
547
554
|
|
|
548
555
|
|
|
@@ -573,7 +580,7 @@ def set_value(ref: str, value: Any) -> EventSpec:
|
|
|
573
580
|
return server_side(
|
|
574
581
|
"_set_value",
|
|
575
582
|
get_fn_signature(set_value),
|
|
576
|
-
ref=
|
|
583
|
+
ref=LiteralVar.create(format.format_ref(ref)),
|
|
577
584
|
value=value,
|
|
578
585
|
)
|
|
579
586
|
|
|
@@ -671,9 +678,9 @@ def set_clipboard(content: str) -> EventSpec:
|
|
|
671
678
|
|
|
672
679
|
|
|
673
680
|
def download(
|
|
674
|
-
url: str |
|
|
675
|
-
filename: Optional[str |
|
|
676
|
-
data: str | bytes |
|
|
681
|
+
url: str | ImmutableVar | None = None,
|
|
682
|
+
filename: Optional[str | ImmutableVar] = None,
|
|
683
|
+
data: str | bytes | ImmutableVar | None = None,
|
|
677
684
|
) -> EventSpec:
|
|
678
685
|
"""Download the file at a given path or with the specified data.
|
|
679
686
|
|
|
@@ -709,21 +716,17 @@ def download(
|
|
|
709
716
|
if isinstance(data, str):
|
|
710
717
|
# Caller provided a plain text string to download.
|
|
711
718
|
url = "data:text/plain," + urllib.parse.quote(data)
|
|
712
|
-
elif isinstance(data,
|
|
719
|
+
elif isinstance(data, ImmutableVar):
|
|
713
720
|
# Need to check on the frontend if the Var already looks like a data: URI.
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
_var_type=bool,
|
|
720
|
-
_var_is_string=False,
|
|
721
|
-
_var_full_name_needs_state_prefix=False,
|
|
722
|
-
)
|
|
721
|
+
|
|
722
|
+
is_data_url = (data.js_type() == "string") & (
|
|
723
|
+
data.to(str).startswith("data:")
|
|
724
|
+
) # type: ignore
|
|
725
|
+
|
|
723
726
|
# If it's a data: URI, use it as is, otherwise convert the Var to JSON in a data: URI.
|
|
724
727
|
url = cond( # type: ignore
|
|
725
728
|
is_data_url,
|
|
726
|
-
data,
|
|
729
|
+
data.to(str),
|
|
727
730
|
"data:text/plain," + data.to_string(), # type: ignore
|
|
728
731
|
)
|
|
729
732
|
elif isinstance(data, bytes):
|
|
@@ -757,11 +760,13 @@ def _callback_arg_spec(eval_result):
|
|
|
757
760
|
|
|
758
761
|
def call_script(
|
|
759
762
|
javascript_code: str | Var[str],
|
|
760
|
-
callback:
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
763
|
+
callback: (
|
|
764
|
+
EventSpec
|
|
765
|
+
| EventHandler
|
|
766
|
+
| Callable
|
|
767
|
+
| List[EventSpec | EventHandler | Callable]
|
|
768
|
+
| None
|
|
769
|
+
) = None,
|
|
765
770
|
) -> EventSpec:
|
|
766
771
|
"""Create an event handler that executes arbitrary javascript code.
|
|
767
772
|
|
|
@@ -830,7 +835,7 @@ def call_event_handler(
|
|
|
830
835
|
arg_spec: The lambda that define the argument(s) to pass to the event handler.
|
|
831
836
|
|
|
832
837
|
Raises:
|
|
833
|
-
|
|
838
|
+
EventHandlerArgMismatch: if number of arguments expected by event_handler doesn't match the spec.
|
|
834
839
|
|
|
835
840
|
Returns:
|
|
836
841
|
The event spec from calling the event handler.
|
|
@@ -842,13 +847,16 @@ def call_event_handler(
|
|
|
842
847
|
return event_handler.add_args(*parsed_args)
|
|
843
848
|
|
|
844
849
|
args = inspect.getfullargspec(event_handler.fn).args
|
|
845
|
-
|
|
850
|
+
n_args = len(args) - 1 # subtract 1 for bound self arg
|
|
851
|
+
if n_args == len(parsed_args):
|
|
846
852
|
return event_handler(*parsed_args) # type: ignore
|
|
847
853
|
else:
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
f"
|
|
851
|
-
|
|
854
|
+
raise EventHandlerArgMismatch(
|
|
855
|
+
"The number of arguments accepted by "
|
|
856
|
+
f"{event_handler.fn.__qualname__} ({n_args}) "
|
|
857
|
+
"does not match the arguments passed by the event trigger: "
|
|
858
|
+
f"{[str(v) for v in parsed_args]}\n"
|
|
859
|
+
"See https://reflex.dev/docs/events/event-arguments/"
|
|
852
860
|
)
|
|
853
861
|
|
|
854
862
|
|
|
@@ -865,68 +873,68 @@ def parse_args_spec(arg_spec: ArgsSpec):
|
|
|
865
873
|
annotations = get_type_hints(arg_spec)
|
|
866
874
|
return arg_spec(
|
|
867
875
|
*[
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
_var_type=annotations.get(l_arg, FrontendEvent),
|
|
871
|
-
_var_is_local=True,
|
|
876
|
+
ImmutableVar(f"_{l_arg}").to(
|
|
877
|
+
ObjectVar, annotations.get(l_arg, FrontendEvent)
|
|
872
878
|
)
|
|
873
879
|
for l_arg in spec.args
|
|
874
880
|
]
|
|
875
881
|
)
|
|
876
882
|
|
|
877
883
|
|
|
878
|
-
def call_event_fn(fn: Callable,
|
|
884
|
+
def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | ImmutableVar:
|
|
879
885
|
"""Call a function to a list of event specs.
|
|
880
886
|
|
|
881
887
|
The function should return a single EventSpec, a list of EventSpecs, or a
|
|
882
|
-
single Var.
|
|
883
|
-
|
|
888
|
+
single Var. The function signature must match the passed arg_spec or
|
|
889
|
+
EventFnArgsMismatch will be raised.
|
|
884
890
|
|
|
885
891
|
Args:
|
|
886
892
|
fn: The function to call.
|
|
887
|
-
|
|
893
|
+
arg_spec: The argument spec for the event trigger.
|
|
888
894
|
|
|
889
895
|
Returns:
|
|
890
896
|
The event specs from calling the function or a Var.
|
|
891
897
|
|
|
892
898
|
Raises:
|
|
893
|
-
|
|
899
|
+
EventFnArgMismatch: If the function signature doesn't match the arg spec.
|
|
900
|
+
EventHandlerValueError: If the lambda returns an unusable value.
|
|
894
901
|
"""
|
|
895
902
|
# Import here to avoid circular imports.
|
|
896
903
|
from reflex.event import EventHandler, EventSpec
|
|
897
904
|
from reflex.utils.exceptions import EventHandlerValueError
|
|
898
905
|
|
|
899
|
-
#
|
|
900
|
-
|
|
906
|
+
# Check that fn signature matches arg_spec
|
|
907
|
+
fn_args = inspect.getfullargspec(fn).args
|
|
908
|
+
n_fn_args = len(fn_args)
|
|
909
|
+
if isinstance(fn, types.MethodType):
|
|
910
|
+
n_fn_args -= 1 # subtract 1 for bound self arg
|
|
911
|
+
parsed_args = parse_args_spec(arg_spec)
|
|
912
|
+
if len(parsed_args) != n_fn_args:
|
|
913
|
+
raise EventFnArgMismatch(
|
|
914
|
+
"The number of arguments accepted by "
|
|
915
|
+
f"{fn} ({n_fn_args}) "
|
|
916
|
+
"does not match the arguments passed by the event trigger: "
|
|
917
|
+
f"{[str(v) for v in parsed_args]}\n"
|
|
918
|
+
"See https://reflex.dev/docs/events/event-arguments/"
|
|
919
|
+
)
|
|
901
920
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
else:
|
|
905
|
-
# Call the lambda.
|
|
906
|
-
if len(args) == 0:
|
|
907
|
-
out = fn()
|
|
908
|
-
elif len(args) == 1:
|
|
909
|
-
out = fn(arg)
|
|
910
|
-
else:
|
|
911
|
-
raise EventHandlerValueError(f"Lambda {fn} must have 0 or 1 arguments.")
|
|
921
|
+
# Call the function with the parsed args.
|
|
922
|
+
out = fn(*parsed_args)
|
|
912
923
|
|
|
913
924
|
# If the function returns a Var, assume it's an EventChain and render it directly.
|
|
914
|
-
if isinstance(out,
|
|
925
|
+
if isinstance(out, ImmutableVar):
|
|
915
926
|
return out
|
|
916
927
|
|
|
917
928
|
# Convert the output to a list.
|
|
918
|
-
if not isinstance(out,
|
|
929
|
+
if not isinstance(out, list):
|
|
919
930
|
out = [out]
|
|
920
931
|
|
|
921
932
|
# Convert any event specs to event specs.
|
|
922
933
|
events = []
|
|
923
934
|
for e in out:
|
|
924
|
-
# Convert handlers to event specs.
|
|
925
935
|
if isinstance(e, EventHandler):
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
elif len(args) == 1:
|
|
929
|
-
e = e(arg) # type: ignore
|
|
936
|
+
# An un-called EventHandler gets all of the args of the event trigger.
|
|
937
|
+
e = call_event_handler(e, arg_spec)
|
|
930
938
|
|
|
931
939
|
# Make sure the event spec is valid.
|
|
932
940
|
if not isinstance(e, EventSpec):
|
|
@@ -941,7 +949,9 @@ def call_event_fn(fn: Callable, arg: Union[Var, ArgsSpec]) -> list[EventSpec] |
|
|
|
941
949
|
return events
|
|
942
950
|
|
|
943
951
|
|
|
944
|
-
def get_handler_args(
|
|
952
|
+
def get_handler_args(
|
|
953
|
+
event_spec: EventSpec,
|
|
954
|
+
) -> tuple[tuple[ImmutableVar, ImmutableVar], ...]:
|
|
945
955
|
"""Get the handler args for the given event spec.
|
|
946
956
|
|
|
947
957
|
Args:
|
reflex/experimental/__init__.py
CHANGED
|
@@ -8,7 +8,6 @@ from reflex.components.sonner.toast import toast as toast
|
|
|
8
8
|
|
|
9
9
|
from ..utils.console import warn
|
|
10
10
|
from . import hooks as hooks
|
|
11
|
-
from . import vars as vars
|
|
12
11
|
from .assets import asset as asset
|
|
13
12
|
from .client_state import ClientStateVar as ClientStateVar
|
|
14
13
|
from .layout import layout as layout
|
|
@@ -33,19 +32,39 @@ class ExperimentalNamespace(SimpleNamespace):
|
|
|
33
32
|
Returns:
|
|
34
33
|
The toast namespace.
|
|
35
34
|
"""
|
|
36
|
-
|
|
37
|
-
_EMITTED_PROMOTION_WARNINGS.add("toast")
|
|
38
|
-
warn(f"`rx._x.toast` was promoted to `rx.toast`.")
|
|
35
|
+
self.register_component_warning("toast")
|
|
39
36
|
return toast
|
|
40
37
|
|
|
38
|
+
@property
|
|
39
|
+
def progress(self):
|
|
40
|
+
"""Temporary property returning the toast namespace.
|
|
41
|
+
|
|
42
|
+
Remove this property when toast is fully promoted.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
The toast namespace.
|
|
46
|
+
"""
|
|
47
|
+
self.register_component_warning("progress")
|
|
48
|
+
return progress
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def register_component_warning(component_name: str):
|
|
52
|
+
"""Add component to emitted warnings and throw a warning if it
|
|
53
|
+
doesn't exist.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
component_name: name of the component.
|
|
57
|
+
"""
|
|
58
|
+
if component_name not in _EMITTED_PROMOTION_WARNINGS:
|
|
59
|
+
_EMITTED_PROMOTION_WARNINGS.add(component_name)
|
|
60
|
+
warn(f"`rx._x.{component_name}` was promoted to `rx.{component_name}`.")
|
|
61
|
+
|
|
41
62
|
|
|
42
63
|
_x = ExperimentalNamespace(
|
|
43
64
|
asset=asset,
|
|
44
65
|
client_state=ClientStateVar.create,
|
|
45
66
|
hooks=hooks,
|
|
46
|
-
vars=vars,
|
|
47
67
|
layout=layout,
|
|
48
|
-
progress=progress,
|
|
49
68
|
PropsBase=PropsBase,
|
|
50
69
|
run_in_thread=run_in_thread,
|
|
51
70
|
)
|
|
@@ -4,12 +4,17 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import dataclasses
|
|
6
6
|
import sys
|
|
7
|
-
from typing import Any, Callable,
|
|
7
|
+
from typing import Any, Callable, Union
|
|
8
8
|
|
|
9
9
|
from reflex import constants
|
|
10
10
|
from reflex.event import EventChain, EventHandler, EventSpec, call_script
|
|
11
|
+
from reflex.ivars.base import ImmutableVar, LiteralVar
|
|
12
|
+
from reflex.ivars.function import FunctionVar
|
|
11
13
|
from reflex.utils.imports import ImportVar
|
|
12
|
-
from reflex.vars import
|
|
14
|
+
from reflex.vars import (
|
|
15
|
+
VarData,
|
|
16
|
+
get_unique_variable_name,
|
|
17
|
+
)
|
|
13
18
|
|
|
14
19
|
NoValue = object()
|
|
15
20
|
|
|
@@ -33,36 +38,19 @@ def _client_state_ref(var_name: str) -> str:
|
|
|
33
38
|
|
|
34
39
|
@dataclasses.dataclass(
|
|
35
40
|
eq=False,
|
|
41
|
+
frozen=True,
|
|
36
42
|
**{"slots": True} if sys.version_info >= (3, 10) else {},
|
|
37
43
|
)
|
|
38
|
-
class ClientStateVar(
|
|
44
|
+
class ClientStateVar(ImmutableVar):
|
|
39
45
|
"""A Var that exists on the client via useState."""
|
|
40
46
|
|
|
41
|
-
# The name of the var.
|
|
42
|
-
_var_name: str = dataclasses.field()
|
|
43
|
-
|
|
44
47
|
# Track the names of the getters and setters
|
|
45
|
-
_setter_name: str = dataclasses.field()
|
|
46
|
-
_getter_name: str = dataclasses.field()
|
|
48
|
+
_setter_name: str = dataclasses.field(default="")
|
|
49
|
+
_getter_name: str = dataclasses.field(default="")
|
|
47
50
|
|
|
48
51
|
# Whether to add the var and setter to the global `refs` object for use in any Component.
|
|
49
52
|
_global_ref: bool = dataclasses.field(default=True)
|
|
50
53
|
|
|
51
|
-
# The type of the var.
|
|
52
|
-
_var_type: Type = dataclasses.field(default=Any)
|
|
53
|
-
|
|
54
|
-
# Whether this is a local javascript variable.
|
|
55
|
-
_var_is_local: bool = dataclasses.field(default=False)
|
|
56
|
-
|
|
57
|
-
# Whether the var is a string literal.
|
|
58
|
-
_var_is_string: bool = dataclasses.field(default=False)
|
|
59
|
-
|
|
60
|
-
# _var_full_name should be prefixed with _var_state
|
|
61
|
-
_var_full_name_needs_state_prefix: bool = dataclasses.field(default=False)
|
|
62
|
-
|
|
63
|
-
# Extra metadata associated with the Var
|
|
64
|
-
_var_data: Optional[VarData] = dataclasses.field(default=None)
|
|
65
|
-
|
|
66
54
|
def __hash__(self) -> int:
|
|
67
55
|
"""Define a hash function for a var.
|
|
68
56
|
|
|
@@ -109,17 +97,16 @@ class ClientStateVar(Var):
|
|
|
109
97
|
var_name = get_unique_variable_name()
|
|
110
98
|
assert isinstance(var_name, str), "var_name must be a string."
|
|
111
99
|
if default is NoValue:
|
|
112
|
-
default_var =
|
|
113
|
-
|
|
114
|
-
default_var = Var.create_safe(
|
|
115
|
-
default,
|
|
116
|
-
_var_is_string=isinstance(default, str),
|
|
100
|
+
default_var = ImmutableVar.create_safe(
|
|
101
|
+
"", _var_is_local=False, _var_is_string=False
|
|
117
102
|
)
|
|
103
|
+
elif not isinstance(default, ImmutableVar):
|
|
104
|
+
default_var = LiteralVar.create(default)
|
|
118
105
|
else:
|
|
119
106
|
default_var = default
|
|
120
107
|
setter_name = f"set{var_name.capitalize()}"
|
|
121
108
|
hooks = {
|
|
122
|
-
f"const [{var_name}, {setter_name}] = useState({default_var
|
|
109
|
+
f"const [{var_name}, {setter_name}] = useState({str(default_var)})": None,
|
|
123
110
|
}
|
|
124
111
|
imports = {
|
|
125
112
|
"react": [ImportVar(tag="useState")],
|
|
@@ -133,12 +120,10 @@ class ClientStateVar(Var):
|
|
|
133
120
|
_setter_name=setter_name,
|
|
134
121
|
_getter_name=var_name,
|
|
135
122
|
_global_ref=global_ref,
|
|
136
|
-
_var_is_local=False,
|
|
137
|
-
_var_is_string=False,
|
|
138
123
|
_var_type=default_var._var_type,
|
|
139
124
|
_var_data=VarData.merge(
|
|
140
125
|
default_var._var_data,
|
|
141
|
-
VarData(
|
|
126
|
+
VarData(
|
|
142
127
|
hooks=hooks,
|
|
143
128
|
imports=imports,
|
|
144
129
|
),
|
|
@@ -146,7 +131,7 @@ class ClientStateVar(Var):
|
|
|
146
131
|
)
|
|
147
132
|
|
|
148
133
|
@property
|
|
149
|
-
def value(self) ->
|
|
134
|
+
def value(self) -> ImmutableVar:
|
|
150
135
|
"""Get a placeholder for the Var.
|
|
151
136
|
|
|
152
137
|
This property can only be rendered on the frontend.
|
|
@@ -157,12 +142,10 @@ class ClientStateVar(Var):
|
|
|
157
142
|
an accessor for the client state variable.
|
|
158
143
|
"""
|
|
159
144
|
return (
|
|
160
|
-
|
|
145
|
+
ImmutableVar.create_safe(
|
|
161
146
|
_client_state_ref(self._getter_name)
|
|
162
147
|
if self._global_ref
|
|
163
|
-
else self._getter_name
|
|
164
|
-
_var_is_local=False,
|
|
165
|
-
_var_is_string=False,
|
|
148
|
+
else self._getter_name
|
|
166
149
|
)
|
|
167
150
|
.to(self._var_type)
|
|
168
151
|
._replace(
|
|
@@ -172,7 +155,7 @@ class ClientStateVar(Var):
|
|
|
172
155
|
)
|
|
173
156
|
)
|
|
174
157
|
|
|
175
|
-
def set_value(self, value: Any = NoValue) ->
|
|
158
|
+
def set_value(self, value: Any = NoValue) -> ImmutableVar:
|
|
176
159
|
"""Set the value of the client state variable.
|
|
177
160
|
|
|
178
161
|
This property can only be attached to a frontend event trigger.
|
|
@@ -191,29 +174,22 @@ class ClientStateVar(Var):
|
|
|
191
174
|
else self._setter_name
|
|
192
175
|
)
|
|
193
176
|
if value is not NoValue:
|
|
177
|
+
import re
|
|
178
|
+
|
|
194
179
|
# This is a hack to make it work like an EventSpec taking an arg
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
setter = f"({arg}) => {setter}({value
|
|
201
|
-
return (
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
_var_is_string=False,
|
|
206
|
-
)
|
|
207
|
-
.to(EventChain)
|
|
208
|
-
._replace(
|
|
209
|
-
merge_var_data=VarData( # type: ignore
|
|
210
|
-
imports=_refs_import if self._global_ref else {}
|
|
211
|
-
)
|
|
212
|
-
)
|
|
213
|
-
)
|
|
180
|
+
value_str = str(LiteralVar.create(value))
|
|
181
|
+
|
|
182
|
+
# remove patterns of ["*"] from the value_str using regex
|
|
183
|
+
arg = re.sub(r"\[\".*\"\]", "", value_str)
|
|
184
|
+
|
|
185
|
+
setter = f"({arg}) => {setter}({str(value)})"
|
|
186
|
+
return ImmutableVar(
|
|
187
|
+
_var_name=setter,
|
|
188
|
+
_var_data=VarData(imports=_refs_import if self._global_ref else {}),
|
|
189
|
+
).to(FunctionVar, EventChain)
|
|
214
190
|
|
|
215
191
|
@property
|
|
216
|
-
def set(self) ->
|
|
192
|
+
def set(self) -> ImmutableVar:
|
|
217
193
|
"""Set the value of the client state variable.
|
|
218
194
|
|
|
219
195
|
This property can only be attached to a frontend event trigger.
|
reflex/experimental/hooks.py
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from reflex.ivars.base import ImmutableVar
|
|
5
6
|
from reflex.utils.imports import ImportVar
|
|
6
|
-
from reflex.vars import
|
|
7
|
+
from reflex.vars import VarData
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def _compose_react_imports(tags: list[str]) -> dict[str, list[ImportVar]]:
|
|
10
11
|
return {"react": [ImportVar(tag=tag) for tag in tags]}
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
def const(name, value) ->
|
|
14
|
+
def const(name, value) -> ImmutableVar:
|
|
14
15
|
"""Create a constant Var.
|
|
15
16
|
|
|
16
17
|
Args:
|
|
@@ -21,13 +22,11 @@ def const(name, value) -> Var:
|
|
|
21
22
|
The constant Var.
|
|
22
23
|
"""
|
|
23
24
|
if isinstance(name, list):
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
)
|
|
27
|
-
return Var.create_safe(f"const {name} = {value}", _var_is_string=False)
|
|
25
|
+
return ImmutableVar.create_safe(f"const [{', '.join(name)}] = {value}")
|
|
26
|
+
return ImmutableVar.create_safe(f"const {name} = {value}")
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
def useCallback(func, deps) ->
|
|
29
|
+
def useCallback(func, deps) -> ImmutableVar:
|
|
31
30
|
"""Create a useCallback hook with a function and dependencies.
|
|
32
31
|
|
|
33
32
|
Args:
|
|
@@ -37,14 +36,13 @@ def useCallback(func, deps) -> Var:
|
|
|
37
36
|
Returns:
|
|
38
37
|
The useCallback hook.
|
|
39
38
|
"""
|
|
40
|
-
return
|
|
39
|
+
return ImmutableVar.create_safe(
|
|
41
40
|
f"useCallback({func}, {deps})" if deps else f"useCallback({func})",
|
|
42
|
-
_var_is_string=False,
|
|
43
41
|
_var_data=VarData(imports=_compose_react_imports(["useCallback"])),
|
|
44
42
|
)
|
|
45
43
|
|
|
46
44
|
|
|
47
|
-
def useContext(context) ->
|
|
45
|
+
def useContext(context) -> ImmutableVar:
|
|
48
46
|
"""Create a useContext hook with a context.
|
|
49
47
|
|
|
50
48
|
Args:
|
|
@@ -53,14 +51,13 @@ def useContext(context) -> Var:
|
|
|
53
51
|
Returns:
|
|
54
52
|
The useContext hook.
|
|
55
53
|
"""
|
|
56
|
-
return
|
|
54
|
+
return ImmutableVar.create_safe(
|
|
57
55
|
f"useContext({context})",
|
|
58
|
-
_var_is_string=False,
|
|
59
56
|
_var_data=VarData(imports=_compose_react_imports(["useContext"])),
|
|
60
57
|
)
|
|
61
58
|
|
|
62
59
|
|
|
63
|
-
def useRef(default) ->
|
|
60
|
+
def useRef(default) -> ImmutableVar:
|
|
64
61
|
"""Create a useRef hook with a default value.
|
|
65
62
|
|
|
66
63
|
Args:
|
|
@@ -69,14 +66,13 @@ def useRef(default) -> Var:
|
|
|
69
66
|
Returns:
|
|
70
67
|
The useRef hook.
|
|
71
68
|
"""
|
|
72
|
-
return
|
|
69
|
+
return ImmutableVar.create_safe(
|
|
73
70
|
f"useRef({default})",
|
|
74
|
-
_var_is_string=False,
|
|
75
71
|
_var_data=VarData(imports=_compose_react_imports(["useRef"])),
|
|
76
72
|
)
|
|
77
73
|
|
|
78
74
|
|
|
79
|
-
def useState(var_name, default=None) ->
|
|
75
|
+
def useState(var_name, default=None) -> ImmutableVar:
|
|
80
76
|
"""Create a useState hook with a variable name and setter name.
|
|
81
77
|
|
|
82
78
|
Args:
|
|
@@ -88,9 +84,8 @@ def useState(var_name, default=None) -> Var:
|
|
|
88
84
|
"""
|
|
89
85
|
return const(
|
|
90
86
|
[var_name, f"set{var_name.capitalize()}"],
|
|
91
|
-
|
|
87
|
+
ImmutableVar.create_safe(
|
|
92
88
|
f"useState({default})",
|
|
93
|
-
_var_is_string=False,
|
|
94
89
|
_var_data=VarData(imports=_compose_react_imports(["useState"])),
|
|
95
90
|
),
|
|
96
91
|
)
|