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.

Files changed (237) hide show
  1. reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -4
  2. reflex/.templates/jinja/web/utils/context.js.jinja2 +1 -1
  3. reflex/.templates/jinja/web/utils/theme.js.jinja2 +1 -1
  4. reflex/__init__.py +3 -2
  5. reflex/__init__.pyi +2 -2
  6. reflex/app.py +43 -9
  7. reflex/base.py +3 -2
  8. reflex/compiler/compiler.py +6 -6
  9. reflex/compiler/utils.py +5 -3
  10. reflex/components/base/app_wrap.py +2 -4
  11. reflex/components/base/app_wrap.pyi +17 -17
  12. reflex/components/base/bare.py +7 -4
  13. reflex/components/base/body.pyi +17 -17
  14. reflex/components/base/document.pyi +81 -81
  15. reflex/components/base/error_boundary.py +10 -8
  16. reflex/components/base/error_boundary.pyi +20 -19
  17. reflex/components/base/fragment.pyi +17 -17
  18. reflex/components/base/head.pyi +33 -33
  19. reflex/components/base/link.pyi +34 -33
  20. reflex/components/base/meta.pyi +65 -65
  21. reflex/components/base/script.py +2 -1
  22. reflex/components/base/script.pyi +21 -20
  23. reflex/components/component.py +116 -145
  24. reflex/components/core/banner.py +59 -60
  25. reflex/components/core/banner.pyi +86 -150
  26. reflex/components/core/client_side_routing.py +2 -1
  27. reflex/components/core/client_side_routing.pyi +34 -33
  28. reflex/components/core/clipboard.py +2 -2
  29. reflex/components/core/clipboard.pyi +19 -18
  30. reflex/components/core/cond.py +21 -44
  31. reflex/components/core/debounce.py +6 -8
  32. reflex/components/core/debounce.pyi +19 -18
  33. reflex/components/core/foreach.py +5 -14
  34. reflex/components/core/html.pyi +18 -17
  35. reflex/components/core/match.py +36 -43
  36. reflex/components/core/upload.py +32 -25
  37. reflex/components/core/upload.pyi +84 -73
  38. reflex/components/datadisplay/code.py +55 -28
  39. reflex/components/datadisplay/code.pyi +20 -17
  40. reflex/components/datadisplay/dataeditor.py +17 -11
  41. reflex/components/datadisplay/dataeditor.pyi +34 -33
  42. reflex/components/el/__init__.py +0 -1
  43. reflex/components/el/__init__.pyi +0 -11
  44. reflex/components/el/element.pyi +17 -17
  45. reflex/components/el/elements/__init__.py +1 -7
  46. reflex/components/el/elements/__init__.pyi +1 -15
  47. reflex/components/el/elements/base.pyi +18 -17
  48. reflex/components/el/elements/forms.py +24 -31
  49. reflex/components/el/elements/forms.pyi +237 -236
  50. reflex/components/el/elements/inline.pyi +450 -449
  51. reflex/components/el/elements/media.py +0 -21
  52. reflex/components/el/elements/media.pyi +338 -337
  53. reflex/components/el/elements/metadata.py +3 -2
  54. reflex/components/el/elements/metadata.pyi +98 -97
  55. reflex/components/el/elements/other.pyi +114 -113
  56. reflex/components/el/elements/scripts.pyi +50 -49
  57. reflex/components/el/elements/sectioning.pyi +242 -241
  58. reflex/components/el/elements/tables.pyi +162 -161
  59. reflex/components/el/elements/typography.pyi +242 -241
  60. reflex/components/gridjs/datatable.py +13 -14
  61. reflex/components/gridjs/datatable.pyi +34 -33
  62. reflex/components/lucide/icon.py +2 -126
  63. reflex/components/lucide/icon.pyi +34 -142
  64. reflex/components/markdown/markdown.py +30 -35
  65. reflex/components/markdown/markdown.pyi +29 -32
  66. reflex/components/moment/moment.pyi +19 -18
  67. reflex/components/next/base.pyi +17 -17
  68. reflex/components/next/image.py +0 -4
  69. reflex/components/next/image.pyi +20 -19
  70. reflex/components/next/link.pyi +18 -17
  71. reflex/components/next/video.pyi +18 -17
  72. reflex/components/plotly/plotly.py +16 -28
  73. reflex/components/plotly/plotly.pyi +36 -35
  74. reflex/components/props.py +21 -10
  75. reflex/components/radix/__init__.pyi +1 -1
  76. reflex/components/radix/primitives/__init__.pyi +0 -1
  77. reflex/components/radix/primitives/accordion.py +7 -8
  78. reflex/components/radix/primitives/accordion.pyi +117 -116
  79. reflex/components/radix/primitives/base.pyi +34 -33
  80. reflex/components/radix/primitives/drawer.pyi +169 -168
  81. reflex/components/radix/primitives/form.pyi +168 -167
  82. reflex/components/radix/primitives/progress.pyi +82 -81
  83. reflex/components/radix/primitives/slider.pyi +84 -83
  84. reflex/components/radix/themes/base.py +8 -4
  85. reflex/components/radix/themes/base.pyi +114 -113
  86. reflex/components/radix/themes/color_mode.py +12 -21
  87. reflex/components/radix/themes/color_mode.pyi +67 -67
  88. reflex/components/radix/themes/components/__init__.pyi +1 -0
  89. reflex/components/radix/themes/components/alert_dialog.pyi +118 -117
  90. reflex/components/radix/themes/components/aspect_ratio.pyi +18 -17
  91. reflex/components/radix/themes/components/avatar.pyi +18 -17
  92. reflex/components/radix/themes/components/badge.pyi +18 -17
  93. reflex/components/radix/themes/components/button.pyi +18 -17
  94. reflex/components/radix/themes/components/callout.pyi +82 -81
  95. reflex/components/radix/themes/components/card.pyi +18 -17
  96. reflex/components/radix/themes/components/checkbox.py +2 -3
  97. reflex/components/radix/themes/components/checkbox.pyi +53 -52
  98. reflex/components/radix/themes/components/checkbox_cards.pyi +34 -33
  99. reflex/components/radix/themes/components/checkbox_group.pyi +34 -33
  100. reflex/components/radix/themes/components/context_menu.pyi +140 -139
  101. reflex/components/radix/themes/components/data_list.py +5 -0
  102. reflex/components/radix/themes/components/data_list.pyi +71 -65
  103. reflex/components/radix/themes/components/dialog.pyi +121 -120
  104. reflex/components/radix/themes/components/dropdown_menu.pyi +142 -141
  105. reflex/components/radix/themes/components/hover_card.pyi +68 -67
  106. reflex/components/radix/themes/components/icon_button.py +2 -1
  107. reflex/components/radix/themes/components/icon_button.pyi +18 -17
  108. reflex/components/radix/themes/components/inset.pyi +18 -17
  109. reflex/components/radix/themes/components/popover.pyi +73 -72
  110. reflex/components/radix/themes/components/progress.pyi +18 -17
  111. reflex/components/radix/themes/components/radio.pyi +18 -17
  112. reflex/components/radix/themes/components/radio_cards.pyi +35 -34
  113. reflex/components/radix/themes/components/radio_group.py +35 -31
  114. reflex/components/radix/themes/components/radio_group.pyi +73 -66
  115. reflex/components/radix/themes/components/scroll_area.pyi +18 -17
  116. reflex/components/radix/themes/components/segmented_control.pyi +35 -34
  117. reflex/components/radix/themes/components/select.py +2 -1
  118. reflex/components/radix/themes/components/select.pyi +155 -154
  119. reflex/components/radix/themes/components/separator.py +2 -3
  120. reflex/components/radix/themes/components/separator.pyi +18 -17
  121. reflex/components/radix/themes/components/skeleton.pyi +18 -17
  122. reflex/components/radix/themes/components/slider.py +2 -1
  123. reflex/components/radix/themes/components/slider.pyi +20 -19
  124. reflex/components/radix/themes/components/spinner.pyi +18 -17
  125. reflex/components/radix/themes/components/switch.pyi +19 -18
  126. reflex/components/radix/themes/components/table.pyi +114 -113
  127. reflex/components/radix/themes/components/tabs.pyi +84 -83
  128. reflex/components/radix/themes/components/text_area.pyi +21 -20
  129. reflex/components/radix/themes/components/text_field.py +0 -79
  130. reflex/components/radix/themes/components/text_field.pyi +57 -63
  131. reflex/components/radix/themes/components/tooltip.pyi +21 -20
  132. reflex/components/radix/themes/layout/base.pyi +18 -17
  133. reflex/components/radix/themes/layout/box.pyi +18 -17
  134. reflex/components/radix/themes/layout/center.pyi +18 -17
  135. reflex/components/radix/themes/layout/container.py +2 -3
  136. reflex/components/radix/themes/layout/container.pyi +18 -17
  137. reflex/components/radix/themes/layout/flex.pyi +18 -17
  138. reflex/components/radix/themes/layout/grid.pyi +18 -17
  139. reflex/components/radix/themes/layout/list.py +5 -4
  140. reflex/components/radix/themes/layout/list.pyi +86 -85
  141. reflex/components/radix/themes/layout/section.py +2 -3
  142. reflex/components/radix/themes/layout/section.pyi +18 -17
  143. reflex/components/radix/themes/layout/spacer.pyi +18 -17
  144. reflex/components/radix/themes/layout/stack.pyi +50 -49
  145. reflex/components/radix/themes/typography/blockquote.pyi +18 -17
  146. reflex/components/radix/themes/typography/code.pyi +18 -17
  147. reflex/components/radix/themes/typography/heading.pyi +18 -17
  148. reflex/components/radix/themes/typography/link.pyi +18 -17
  149. reflex/components/radix/themes/typography/text.pyi +114 -113
  150. reflex/components/react_player/audio.pyi +34 -33
  151. reflex/components/react_player/react_player.pyi +34 -33
  152. reflex/components/react_player/video.pyi +34 -33
  153. reflex/components/recharts/cartesian.py +23 -19
  154. reflex/components/recharts/cartesian.pyi +297 -296
  155. reflex/components/recharts/charts.py +6 -5
  156. reflex/components/recharts/charts.pyi +179 -178
  157. reflex/components/recharts/general.py +8 -7
  158. reflex/components/recharts/general.pyi +82 -81
  159. reflex/components/recharts/polar.py +14 -13
  160. reflex/components/recharts/polar.pyi +76 -75
  161. reflex/components/recharts/recharts.pyi +33 -33
  162. reflex/components/sonner/toast.py +30 -33
  163. reflex/components/sonner/toast.pyi +27 -25
  164. reflex/components/suneditor/editor.py +2 -1
  165. reflex/components/suneditor/editor.pyi +27 -26
  166. reflex/components/tags/iter_tag.py +16 -16
  167. reflex/components/tags/tag.py +8 -10
  168. reflex/constants/base.py +3 -1
  169. reflex/constants/event.py +1 -0
  170. reflex/event.py +89 -79
  171. reflex/experimental/__init__.py +25 -6
  172. reflex/experimental/client_state.py +34 -58
  173. reflex/experimental/hooks.py +13 -18
  174. reflex/experimental/layout.py +5 -5
  175. reflex/experimental/layout.pyi +84 -83
  176. reflex/{experimental/vars → ivars}/__init__.py +0 -1
  177. reflex/ivars/base.py +2180 -0
  178. reflex/ivars/function.py +200 -0
  179. reflex/ivars/number.py +1137 -0
  180. reflex/ivars/object.py +564 -0
  181. reflex/ivars/sequence.py +1601 -0
  182. reflex/model.py +22 -0
  183. reflex/reflex.py +4 -0
  184. reflex/state.py +388 -73
  185. reflex/style.py +52 -34
  186. reflex/testing.py +8 -3
  187. reflex/utils/exceptions.py +12 -0
  188. reflex/utils/exec.py +0 -14
  189. reflex/utils/format.py +74 -223
  190. reflex/utils/net.py +43 -0
  191. reflex/utils/path_ops.py +13 -1
  192. reflex/utils/prerequisites.py +46 -26
  193. reflex/utils/pyi_generator.py +5 -4
  194. reflex/utils/serializers.py +13 -31
  195. reflex/utils/types.py +44 -9
  196. reflex/vars.py +127 -2230
  197. {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/METADATA +4 -6
  198. reflex-0.6.0a1.dist-info/RECORD +384 -0
  199. reflex/.templates/apps/demo/.gitignore +0 -4
  200. reflex/.templates/apps/demo/assets/favicon.ico +0 -0
  201. reflex/.templates/apps/demo/assets/github.svg +0 -10
  202. reflex/.templates/apps/demo/assets/icon.svg +0 -37
  203. reflex/.templates/apps/demo/assets/logo.svg +0 -68
  204. reflex/.templates/apps/demo/assets/paneleft.svg +0 -13
  205. reflex/.templates/apps/demo/code/__init__.py +0 -1
  206. reflex/.templates/apps/demo/code/demo.py +0 -127
  207. reflex/.templates/apps/demo/code/pages/__init__.py +0 -7
  208. reflex/.templates/apps/demo/code/pages/chatapp.py +0 -31
  209. reflex/.templates/apps/demo/code/pages/datatable.py +0 -360
  210. reflex/.templates/apps/demo/code/pages/forms.py +0 -257
  211. reflex/.templates/apps/demo/code/pages/graphing.py +0 -253
  212. reflex/.templates/apps/demo/code/pages/home.py +0 -56
  213. reflex/.templates/apps/demo/code/sidebar.py +0 -178
  214. reflex/.templates/apps/demo/code/state.py +0 -22
  215. reflex/.templates/apps/demo/code/states/form_state.py +0 -40
  216. reflex/.templates/apps/demo/code/states/pie_state.py +0 -47
  217. reflex/.templates/apps/demo/code/styles.py +0 -68
  218. reflex/.templates/apps/demo/code/webui/__init__.py +0 -0
  219. reflex/.templates/apps/demo/code/webui/components/__init__.py +0 -4
  220. reflex/.templates/apps/demo/code/webui/components/chat.py +0 -118
  221. reflex/.templates/apps/demo/code/webui/components/loading_icon.py +0 -19
  222. reflex/.templates/apps/demo/code/webui/components/modal.py +0 -56
  223. reflex/.templates/apps/demo/code/webui/components/navbar.py +0 -70
  224. reflex/.templates/apps/demo/code/webui/components/sidebar.py +0 -66
  225. reflex/.templates/apps/demo/code/webui/state.py +0 -146
  226. reflex/.templates/apps/demo/code/webui/styles.py +0 -88
  227. reflex/experimental/vars/base.py +0 -583
  228. reflex/experimental/vars/function.py +0 -290
  229. reflex/experimental/vars/number.py +0 -1458
  230. reflex/experimental/vars/object.py +0 -804
  231. reflex/experimental/vars/sequence.py +0 -1764
  232. reflex/utils/watch.py +0 -96
  233. reflex/vars.pyi +0 -218
  234. reflex-0.5.10a3.dist-info/RECORD +0 -413
  235. {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/LICENSE +0 -0
  236. {reflex-0.5.10a3.dist-info → reflex-0.6.0a1.dist-info}/WHEEL +0 -0
  237. {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 BaseVar, Var
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 = (Var.create_safe(arg, _var_is_string=False) for arg in 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(Var.create(arg, _var_is_string=isinstance(arg, str)))
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[Var, Var], ...] = ()
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(self, args: Tuple[Tuple[Var, Var], ...]) -> EventSpec:
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: Var) -> EventSpec:
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 = (Var.create_safe(arg, _var_is_string=False) for arg in 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(Var.create(arg, _var_is_string=isinstance(arg, str)))
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
- Var.create_safe("files", _var_is_string=False),
392
- Var.create_safe(
393
- f"filesById[{Var.create_safe(upload_id, _var_is_string=True)._var_name_unwrapped}]",
394
- _var_is_string=False,
395
- )._replace(_var_data=upload_files_context_var_data),
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
- Var.create_safe("upload_id", _var_is_string=False),
399
- Var.create_safe(upload_id, _var_is_string=True),
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, Var):
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
- Var.create_safe("on_upload_progress", _var_is_string=False),
428
- BaseVar(
429
- _var_name=formatted_chain.strip("{}"),
430
- _var_type=EventChain,
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
- Var.create_safe(k, _var_is_string=False),
469
- Var.create_safe(v, _var_is_string=isinstance(v, str)),
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=Var.create_safe(format.format_ref(ref), _var_is_string=True),
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=Var.create_safe(format.format_ref(ref), _var_is_string=True),
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 | Var | None = None,
675
- filename: Optional[str | Var] = None,
676
- data: str | bytes | Var | None = None,
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, Var):
719
+ elif isinstance(data, ImmutableVar):
713
720
  # Need to check on the frontend if the Var already looks like a data: URI.
714
- is_data_url = data._replace(
715
- _var_name=(
716
- f"typeof {data._var_full_name} == 'string' && "
717
- f"{data._var_full_name}.startsWith('data:')"
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: EventSpec
761
- | EventHandler
762
- | Callable
763
- | List[EventSpec | EventHandler | Callable]
764
- | None = None,
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
- ValueError: if number of arguments expected by event_handler doesn't match the spec.
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
- if len(args) == len(["self", *parsed_args]):
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
- source = inspect.getsource(arg_spec) # type: ignore
849
- raise ValueError(
850
- f"number of arguments in {event_handler.fn.__qualname__} "
851
- f"doesn't match the definition of the event trigger '{source.strip().strip(',')}'"
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
- BaseVar(
869
- _var_name=f"_{l_arg}",
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, arg: Union[Var, ArgsSpec]) -> list[EventSpec] | Var:
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. If the function takes in an arg, the arg will be passed to the
883
- function. Otherwise, the function will be called with no args.
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
- arg: The argument to pass to the function.
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
- EventHandlerValueError: If the lambda has an invalid signature.
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
- # Get the args of the lambda.
900
- args = inspect.getfullargspec(fn).args
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
- if isinstance(arg, ArgsSpec):
903
- out = fn(*parse_args_spec(arg)) # type: ignore
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, Var):
925
+ if isinstance(out, ImmutableVar):
915
926
  return out
916
927
 
917
928
  # Convert the output to a list.
918
- if not isinstance(out, List):
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
- if len(args) == 0:
927
- e = e()
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(event_spec: EventSpec) -> tuple[tuple[Var, Var], ...]:
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:
@@ -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
- if "toast" not in _EMITTED_PROMOTION_WARNINGS:
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, Optional, Type, Union
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 Var, VarData, get_unique_variable_name
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(Var):
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 = Var.create_safe("", _var_is_local=False, _var_is_string=False)
113
- elif not isinstance(default, Var):
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._var_name_unwrapped})": None,
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( # type: ignore
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) -> Var:
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
- Var.create_safe(
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) -> Var:
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
- value = Var.create_safe(value, _var_is_string=isinstance(value, str))
196
- if not value._var_is_string and value._var_full_name.startswith("_"):
197
- arg = value._var_name_unwrapped.partition(".")[0]
198
- else:
199
- arg = ""
200
- setter = f"({arg}) => {setter}({value._var_name_unwrapped})"
201
- return (
202
- Var.create_safe(
203
- setter,
204
- _var_is_local=False,
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) -> Var:
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.
@@ -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 Var, VarData
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) -> Var:
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 Var.create_safe(
25
- f"const [{', '.join(name)}] = {value}", _var_is_string=False
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) -> Var:
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 Var.create_safe(
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) -> Var:
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 Var.create_safe(
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) -> Var:
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 Var.create_safe(
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) -> Var:
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
- Var.create_safe(
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
  )