reflex 0.6.0a1__py3-none-any.whl → 0.6.0a2__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 (249) hide show
  1. reflex/.templates/jinja/web/pages/_app.js.jinja2 +1 -1
  2. reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
  3. reflex/__init__.py +8 -2
  4. reflex/__init__.pyi +2 -1
  5. reflex/app.py +4 -2
  6. reflex/base.py +1 -1
  7. reflex/compiler/compiler.py +2 -2
  8. reflex/compiler/utils.py +3 -3
  9. reflex/components/base/app_wrap.py +2 -2
  10. reflex/components/base/app_wrap.pyi +17 -27
  11. reflex/components/base/bare.py +4 -5
  12. reflex/components/base/body.pyi +17 -27
  13. reflex/components/base/document.pyi +81 -131
  14. reflex/components/base/error_boundary.py +6 -7
  15. reflex/components/base/error_boundary.pyi +20 -33
  16. reflex/components/base/fragment.pyi +17 -27
  17. reflex/components/base/head.pyi +33 -53
  18. reflex/components/base/link.py +1 -1
  19. reflex/components/base/link.pyi +33 -54
  20. reflex/components/base/meta.pyi +65 -105
  21. reflex/components/base/script.py +1 -2
  22. reflex/components/base/script.pyi +21 -38
  23. reflex/components/component.py +48 -47
  24. reflex/components/core/banner.py +23 -27
  25. reflex/components/core/banner.pyi +134 -171
  26. reflex/components/core/client_side_routing.py +2 -3
  27. reflex/components/core/client_side_routing.pyi +33 -54
  28. reflex/components/core/clipboard.py +2 -1
  29. reflex/components/core/clipboard.pyi +20 -33
  30. reflex/components/core/cond.py +5 -5
  31. reflex/components/core/debounce.py +5 -5
  32. reflex/components/core/debounce.pyi +20 -33
  33. reflex/components/core/foreach.py +3 -4
  34. reflex/components/core/html.py +1 -1
  35. reflex/components/core/html.pyi +35 -46
  36. reflex/components/core/match.py +17 -17
  37. reflex/components/core/upload.py +17 -23
  38. reflex/components/core/upload.pyi +78 -124
  39. reflex/components/datadisplay/code.py +9 -10
  40. reflex/components/datadisplay/code.pyi +302 -412
  41. reflex/components/datadisplay/dataeditor.py +8 -10
  42. reflex/components/datadisplay/dataeditor.pyi +40 -53
  43. reflex/components/el/element.pyi +17 -27
  44. reflex/components/el/elements/base.py +1 -1
  45. reflex/components/el/elements/base.pyi +34 -45
  46. reflex/components/el/elements/forms.py +16 -16
  47. reflex/components/el/elements/forms.pyi +554 -707
  48. reflex/components/el/elements/inline.py +1 -1
  49. reflex/components/el/elements/inline.pyi +937 -1218
  50. reflex/components/el/elements/media.py +1 -1
  51. reflex/components/el/elements/media.pyi +786 -997
  52. reflex/components/el/elements/metadata.py +3 -6
  53. reflex/components/el/elements/metadata.pyi +181 -242
  54. reflex/components/el/elements/other.py +1 -1
  55. reflex/components/el/elements/other.pyi +235 -306
  56. reflex/components/el/elements/scripts.py +1 -1
  57. reflex/components/el/elements/scripts.pyi +109 -140
  58. reflex/components/el/elements/sectioning.py +0 -2
  59. reflex/components/el/elements/sectioning.pyi +496 -647
  60. reflex/components/el/elements/tables.py +1 -1
  61. reflex/components/el/elements/tables.pyi +351 -452
  62. reflex/components/el/elements/typography.py +1 -1
  63. reflex/components/el/elements/typography.pyi +506 -657
  64. reflex/components/gridjs/datatable.py +6 -9
  65. reflex/components/gridjs/datatable.pyi +35 -56
  66. reflex/components/lucide/icon.py +1 -1
  67. reflex/components/lucide/icon.pyi +33 -54
  68. reflex/components/markdown/markdown.py +26 -31
  69. reflex/components/markdown/markdown.pyi +27 -37
  70. reflex/components/moment/moment.py +13 -12
  71. reflex/components/moment/moment.pyi +23 -35
  72. reflex/components/next/base.pyi +17 -27
  73. reflex/components/next/image.py +1 -1
  74. reflex/components/next/image.pyi +22 -37
  75. reflex/components/next/link.py +1 -1
  76. reflex/components/next/link.pyi +17 -28
  77. reflex/components/next/video.py +1 -1
  78. reflex/components/next/video.pyi +17 -28
  79. reflex/components/plotly/plotly.py +12 -13
  80. reflex/components/plotly/plotly.pyi +39 -54
  81. reflex/components/props.py +1 -1
  82. reflex/components/radix/__init__.pyi +1 -0
  83. reflex/components/radix/primitives/__init__.pyi +1 -0
  84. reflex/components/radix/primitives/accordion.py +4 -4
  85. reflex/components/radix/primitives/accordion.pyi +424 -495
  86. reflex/components/radix/primitives/base.py +1 -1
  87. reflex/components/radix/primitives/base.pyi +33 -54
  88. reflex/components/radix/primitives/drawer.py +1 -1
  89. reflex/components/radix/primitives/drawer.pyi +172 -273
  90. reflex/components/radix/primitives/form.py +1 -1
  91. reflex/components/radix/primitives/form.pyi +257 -364
  92. reflex/components/radix/primitives/progress.py +1 -1
  93. reflex/components/radix/primitives/progress.pyi +231 -282
  94. reflex/components/radix/primitives/slider.py +1 -1
  95. reflex/components/radix/primitives/slider.pyi +87 -138
  96. reflex/components/radix/themes/base.py +3 -24
  97. reflex/components/radix/themes/base.pyi +178 -250
  98. reflex/components/radix/themes/color_mode.py +5 -5
  99. reflex/components/radix/themes/color_mode.pyi +187 -220
  100. reflex/components/radix/themes/components/alert_dialog.py +1 -1
  101. reflex/components/radix/themes/components/alert_dialog.pyi +136 -207
  102. reflex/components/radix/themes/components/aspect_ratio.py +1 -1
  103. reflex/components/radix/themes/components/aspect_ratio.pyi +17 -28
  104. reflex/components/radix/themes/components/avatar.py +1 -1
  105. reflex/components/radix/themes/components/avatar.pyi +70 -81
  106. reflex/components/radix/themes/components/badge.py +1 -1
  107. reflex/components/radix/themes/components/badge.pyi +88 -99
  108. reflex/components/radix/themes/components/button.py +1 -1
  109. reflex/components/radix/themes/components/button.pyi +98 -109
  110. reflex/components/radix/themes/components/callout.py +1 -1
  111. reflex/components/radix/themes/components/callout.pyi +322 -373
  112. reflex/components/radix/themes/components/card.py +1 -1
  113. reflex/components/radix/themes/components/card.pyi +38 -49
  114. reflex/components/radix/themes/components/checkbox.py +1 -2
  115. reflex/components/radix/themes/components/checkbox.pyi +208 -245
  116. reflex/components/radix/themes/components/checkbox_cards.py +1 -1
  117. reflex/components/radix/themes/components/checkbox_cards.pyi +94 -115
  118. reflex/components/radix/themes/components/checkbox_group.py +1 -1
  119. reflex/components/radix/themes/components/checkbox_group.pyi +86 -107
  120. reflex/components/radix/themes/components/context_menu.py +1 -1
  121. reflex/components/radix/themes/components/context_menu.pyi +238 -319
  122. reflex/components/radix/themes/components/data_list.py +1 -1
  123. reflex/components/radix/themes/components/data_list.pyi +130 -171
  124. reflex/components/radix/themes/components/dialog.py +1 -1
  125. reflex/components/radix/themes/components/dialog.pyi +139 -210
  126. reflex/components/radix/themes/components/dropdown_menu.py +1 -1
  127. reflex/components/radix/themes/components/dropdown_menu.pyi +249 -332
  128. reflex/components/radix/themes/components/hover_card.py +1 -1
  129. reflex/components/radix/themes/components/hover_card.pyi +90 -131
  130. reflex/components/radix/themes/components/icon_button.py +2 -3
  131. reflex/components/radix/themes/components/icon_button.pyi +98 -109
  132. reflex/components/radix/themes/components/inset.py +1 -1
  133. reflex/components/radix/themes/components/inset.pyi +47 -58
  134. reflex/components/radix/themes/components/popover.py +1 -1
  135. reflex/components/radix/themes/components/popover.pyi +95 -136
  136. reflex/components/radix/themes/components/progress.py +1 -1
  137. reflex/components/radix/themes/components/progress.pyi +71 -82
  138. reflex/components/radix/themes/components/radio.py +1 -1
  139. reflex/components/radix/themes/components/radio.pyi +69 -80
  140. reflex/components/radix/themes/components/radio_cards.py +1 -1
  141. reflex/components/radix/themes/components/radio_cards.pyi +98 -119
  142. reflex/components/radix/themes/components/radio_group.py +8 -11
  143. reflex/components/radix/themes/components/radio_group.pyi +228 -271
  144. reflex/components/radix/themes/components/scroll_area.py +1 -1
  145. reflex/components/radix/themes/components/scroll_area.pyi +21 -32
  146. reflex/components/radix/themes/components/segmented_control.py +1 -1
  147. reflex/components/radix/themes/components/segmented_control.pyi +90 -113
  148. reflex/components/radix/themes/components/select.py +2 -3
  149. reflex/components/radix/themes/components/select.pyi +374 -471
  150. reflex/components/radix/themes/components/separator.py +1 -2
  151. reflex/components/radix/themes/components/separator.pyi +69 -80
  152. reflex/components/radix/themes/components/skeleton.py +1 -1
  153. reflex/components/radix/themes/components/skeleton.pyi +23 -34
  154. reflex/components/radix/themes/components/slider.py +2 -3
  155. reflex/components/radix/themes/components/slider.pyi +75 -88
  156. reflex/components/radix/themes/components/spinner.py +1 -1
  157. reflex/components/radix/themes/components/spinner.pyi +19 -30
  158. reflex/components/radix/themes/components/switch.py +1 -1
  159. reflex/components/radix/themes/components/switch.pyi +71 -84
  160. reflex/components/radix/themes/components/table.py +1 -1
  161. reflex/components/radix/themes/components/table.pyi +261 -332
  162. reflex/components/radix/themes/components/tabs.py +1 -1
  163. reflex/components/radix/themes/components/tabs.pyi +139 -194
  164. reflex/components/radix/themes/components/text_area.py +1 -1
  165. reflex/components/radix/themes/components/text_area.pyi +96 -111
  166. reflex/components/radix/themes/components/text_field.py +1 -1
  167. reflex/components/radix/themes/components/text_field.pyi +247 -286
  168. reflex/components/radix/themes/components/tooltip.py +1 -1
  169. reflex/components/radix/themes/components/tooltip.pyi +26 -37
  170. reflex/components/radix/themes/layout/__init__.pyi +1 -0
  171. reflex/components/radix/themes/layout/base.py +1 -1
  172. reflex/components/radix/themes/layout/base.pyi +56 -67
  173. reflex/components/radix/themes/layout/box.pyi +34 -45
  174. reflex/components/radix/themes/layout/center.pyi +56 -67
  175. reflex/components/radix/themes/layout/container.py +1 -2
  176. reflex/components/radix/themes/layout/container.pyi +36 -47
  177. reflex/components/radix/themes/layout/flex.py +1 -1
  178. reflex/components/radix/themes/layout/flex.pyi +56 -67
  179. reflex/components/radix/themes/layout/grid.py +1 -1
  180. reflex/components/radix/themes/layout/grid.pyi +64 -75
  181. reflex/components/radix/themes/layout/list.py +5 -6
  182. reflex/components/radix/themes/layout/list.pyi +193 -244
  183. reflex/components/radix/themes/layout/section.py +1 -2
  184. reflex/components/radix/themes/layout/section.pyi +36 -47
  185. reflex/components/radix/themes/layout/spacer.pyi +56 -67
  186. reflex/components/radix/themes/layout/stack.py +1 -1
  187. reflex/components/radix/themes/layout/stack.pyi +128 -159
  188. reflex/components/radix/themes/typography/blockquote.py +1 -1
  189. reflex/components/radix/themes/typography/blockquote.pyi +89 -100
  190. reflex/components/radix/themes/typography/code.py +1 -1
  191. reflex/components/radix/themes/typography/code.pyi +90 -101
  192. reflex/components/radix/themes/typography/heading.py +1 -1
  193. reflex/components/radix/themes/typography/heading.pyi +96 -107
  194. reflex/components/radix/themes/typography/link.py +1 -1
  195. reflex/components/radix/themes/typography/link.pyi +102 -113
  196. reflex/components/radix/themes/typography/text.py +1 -1
  197. reflex/components/radix/themes/typography/text.pyi +501 -572
  198. reflex/components/react_player/audio.pyi +33 -60
  199. reflex/components/react_player/react_player.py +1 -1
  200. reflex/components/react_player/react_player.pyi +33 -60
  201. reflex/components/react_player/video.pyi +33 -60
  202. reflex/components/recharts/cartesian.py +2 -3
  203. reflex/components/recharts/cartesian.pyi +678 -861
  204. reflex/components/recharts/charts.py +4 -5
  205. reflex/components/recharts/charts.pyi +252 -357
  206. reflex/components/recharts/general.py +1 -2
  207. reflex/components/recharts/general.pyi +180 -231
  208. reflex/components/recharts/polar.py +4 -5
  209. reflex/components/recharts/polar.pyi +144 -181
  210. reflex/components/recharts/recharts.pyi +33 -53
  211. reflex/components/sonner/toast.py +16 -17
  212. reflex/components/sonner/toast.pyi +36 -47
  213. reflex/components/suneditor/editor.py +2 -3
  214. reflex/components/suneditor/editor.pyi +55 -78
  215. reflex/components/tags/cond_tag.py +6 -4
  216. reflex/components/tags/iter_tag.py +28 -16
  217. reflex/components/tags/match_tag.py +6 -4
  218. reflex/components/tags/tag.py +40 -23
  219. reflex/event.py +113 -65
  220. reflex/experimental/client_state.py +18 -18
  221. reflex/experimental/hooks.py +16 -16
  222. reflex/experimental/layout.py +5 -5
  223. reflex/experimental/layout.pyi +136 -187
  224. reflex/middleware/hydrate_middleware.py +2 -0
  225. reflex/middleware/middleware.py +3 -3
  226. reflex/state.py +148 -82
  227. reflex/style.py +21 -22
  228. reflex/utils/exceptions.py +16 -0
  229. reflex/utils/format.py +22 -34
  230. reflex/utils/imports.py +16 -73
  231. reflex/utils/prerequisites.py +15 -8
  232. reflex/utils/pyi_generator.py +13 -8
  233. reflex/utils/serializers.py +12 -22
  234. reflex/utils/telemetry.py +2 -1
  235. reflex/utils/types.py +10 -5
  236. reflex/{ivars → vars}/__init__.py +6 -2
  237. reflex/{ivars → vars}/base.py +567 -206
  238. reflex/{ivars → vars}/function.py +15 -19
  239. reflex/{ivars → vars}/number.py +16 -18
  240. reflex/{ivars → vars}/object.py +28 -30
  241. reflex/{ivars → vars}/sequence.py +53 -42
  242. {reflex-0.6.0a1.dist-info → reflex-0.6.0a2.dist-info}/METADATA +2 -2
  243. reflex-0.6.0a2.dist-info/RECORD +382 -0
  244. reflex/.templates/web/components/reflex/chakra_color_mode_provider.js +0 -36
  245. reflex/vars.py +0 -501
  246. reflex-0.6.0a1.dist-info/RECORD +0 -384
  247. {reflex-0.6.0a1.dist-info → reflex-0.6.0a2.dist-info}/LICENSE +0 -0
  248. {reflex-0.6.0a1.dist-info → reflex-0.6.0a2.dist-info}/WHEEL +0 -0
  249. {reflex-0.6.0a1.dist-info → reflex-0.6.0a2.dist-info}/entry_points.txt +0 -0
@@ -2,31 +2,43 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import dataclasses
5
6
  import inspect
6
- from typing import TYPE_CHECKING, Any, Callable, List, Tuple, Type, Union, get_args
7
+ from typing import (
8
+ TYPE_CHECKING,
9
+ Any,
10
+ Callable,
11
+ Iterable,
12
+ Tuple,
13
+ Type,
14
+ Union,
15
+ get_args,
16
+ )
7
17
 
8
18
  from reflex.components.tags.tag import Tag
9
- from reflex.ivars.base import ImmutableVar
10
- from reflex.vars import Var
19
+ from reflex.vars import LiteralArrayVar, Var, get_unique_variable_name
11
20
 
12
21
  if TYPE_CHECKING:
13
22
  from reflex.components.component import Component
14
23
 
15
24
 
25
+ @dataclasses.dataclass()
16
26
  class IterTag(Tag):
17
27
  """An iterator tag."""
18
28
 
19
29
  # The var to iterate over.
20
- iterable: Var[List]
30
+ iterable: Var[Iterable] = dataclasses.field(
31
+ default_factory=lambda: LiteralArrayVar.create([])
32
+ )
21
33
 
22
34
  # The component render function for each item in the iterable.
23
- render_fn: Callable
35
+ render_fn: Callable = dataclasses.field(default_factory=lambda: lambda x: x)
24
36
 
25
37
  # The name of the arg var.
26
- arg_var_name: str
38
+ arg_var_name: str = dataclasses.field(default_factory=get_unique_variable_name)
27
39
 
28
40
  # The name of the index var.
29
- index_var_name: str
41
+ index_var_name: str = dataclasses.field(default_factory=get_unique_variable_name)
30
42
 
31
43
  def get_iterable_var_type(self) -> Type:
32
44
  """Get the type of the iterable var.
@@ -34,7 +46,7 @@ class IterTag(Tag):
34
46
  Returns:
35
47
  The type of the iterable var.
36
48
  """
37
- iterable = self.iterable.upcast()
49
+ iterable = self.iterable
38
50
  try:
39
51
  if iterable._var_type.mro()[0] == dict:
40
52
  # Arg is a tuple of (key, value).
@@ -55,8 +67,8 @@ class IterTag(Tag):
55
67
  Returns:
56
68
  The index var.
57
69
  """
58
- return ImmutableVar(
59
- _var_name=self.index_var_name,
70
+ return Var(
71
+ _js_expr=self.index_var_name,
60
72
  _var_type=int,
61
73
  ).guess_type()
62
74
 
@@ -68,8 +80,8 @@ class IterTag(Tag):
68
80
  Returns:
69
81
  The arg var.
70
82
  """
71
- return ImmutableVar(
72
- _var_name=self.arg_var_name,
83
+ return Var(
84
+ _js_expr=self.arg_var_name,
73
85
  _var_type=self.get_iterable_var_type(),
74
86
  ).guess_type()
75
87
 
@@ -81,8 +93,8 @@ class IterTag(Tag):
81
93
  Returns:
82
94
  The index var.
83
95
  """
84
- return ImmutableVar(
85
- _var_name=self.index_var_name,
96
+ return Var(
97
+ _js_expr=self.index_var_name,
86
98
  _var_type=int,
87
99
  ).guess_type()
88
100
 
@@ -94,8 +106,8 @@ class IterTag(Tag):
94
106
  Returns:
95
107
  The arg var.
96
108
  """
97
- return ImmutableVar(
98
- _var_name=self.arg_var_name,
109
+ return Var(
110
+ _js_expr=self.arg_var_name,
99
111
  _var_type=self.get_iterable_var_type(),
100
112
  ).guess_type()
101
113
 
@@ -1,19 +1,21 @@
1
1
  """Tag to conditionally match cases."""
2
2
 
3
+ import dataclasses
3
4
  from typing import Any, List
4
5
 
5
6
  from reflex.components.tags.tag import Tag
6
- from reflex.vars import Var
7
+ from reflex.vars.base import Var
7
8
 
8
9
 
10
+ @dataclasses.dataclass()
9
11
  class MatchTag(Tag):
10
12
  """A match tag."""
11
13
 
12
14
  # The condition to determine which case to match.
13
- cond: Var[Any]
15
+ cond: Var[Any] = dataclasses.field(default_factory=lambda: Var.create(True))
14
16
 
15
17
  # The list of match cases to be matched.
16
- match_cases: List[Any]
18
+ match_cases: List[Any] = dataclasses.field(default_factory=list)
17
19
 
18
20
  # The catchall case to match.
19
- default: Any
21
+ default: Any = dataclasses.field(default=Var.create(None))
@@ -2,22 +2,23 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Dict, List, Optional, Set, Tuple, Union
5
+ import dataclasses
6
+ from typing import Any, Dict, List, Optional, Tuple, Union
6
7
 
7
- from reflex.base import Base
8
8
  from reflex.event import EventChain
9
- from reflex.ivars.base import ImmutableVar, LiteralVar
10
9
  from reflex.utils import format, types
10
+ from reflex.vars.base import LiteralVar, Var
11
11
 
12
12
 
13
- class Tag(Base):
13
+ @dataclasses.dataclass()
14
+ class Tag:
14
15
  """A React tag."""
15
16
 
16
17
  # The name of the tag.
17
18
  name: str = ""
18
19
 
19
20
  # The props of the tag.
20
- props: Dict[str, Any] = {}
21
+ props: Dict[str, Any] = dataclasses.field(default_factory=dict)
21
22
 
22
23
  # The inner contents of the tag.
23
24
  contents: str = ""
@@ -26,25 +27,18 @@ class Tag(Base):
26
27
  args: Optional[Tuple[str, ...]] = None
27
28
 
28
29
  # Special props that aren't key value pairs.
29
- special_props: Set[ImmutableVar] = set()
30
+ special_props: List[Var] = dataclasses.field(default_factory=list)
30
31
 
31
32
  # The children components.
32
- children: List[Any] = []
33
-
34
- def __init__(self, *args, **kwargs):
35
- """Initialize the tag.
36
-
37
- Args:
38
- *args: Args to initialize the tag.
39
- **kwargs: Kwargs to initialize the tag.
40
- """
41
- # Convert any props to vars.
42
- if "props" in kwargs:
43
- kwargs["props"] = {
44
- name: LiteralVar.create(value)
45
- for name, value in kwargs["props"].items()
46
- }
47
- super().__init__(*args, **kwargs)
33
+ children: List[Any] = dataclasses.field(default_factory=list)
34
+
35
+ def __post_init__(self):
36
+ """Post initialize the tag."""
37
+ object.__setattr__(
38
+ self,
39
+ "props",
40
+ {name: LiteralVar.create(value) for name, value in self.props.items()},
41
+ )
48
42
 
49
43
  def format_props(self) -> List:
50
44
  """Format the tag's props.
@@ -54,6 +48,29 @@ class Tag(Base):
54
48
  """
55
49
  return format.format_props(*self.special_props, **self.props)
56
50
 
51
+ def set(self, **kwargs: Any):
52
+ """Set the tag's fields.
53
+
54
+ Args:
55
+ kwargs: The fields to set.
56
+
57
+ Returns:
58
+ The tag with the fields
59
+ """
60
+ for name, value in kwargs.items():
61
+ setattr(self, name, value)
62
+
63
+ return self
64
+
65
+ def __iter__(self):
66
+ """Iterate over the tag's fields.
67
+
68
+ Yields:
69
+ Tuple[str, Any]: The field name and value.
70
+ """
71
+ for field in dataclasses.fields(self):
72
+ yield field.name, getattr(self, field.name)
73
+
57
74
  def add_props(self, **kwargs: Optional[Any]) -> Tag:
58
75
  """Add props to the tag.
59
76
 
@@ -92,7 +109,7 @@ class Tag(Base):
92
109
  return self
93
110
 
94
111
  @staticmethod
95
- def is_valid_prop(prop: Optional[ImmutableVar]) -> bool:
112
+ def is_valid_prop(prop: Optional[Var]) -> bool:
96
113
  """Check if the prop is valid.
97
114
 
98
115
  Args:
reflex/event.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import dataclasses
5
6
  import inspect
6
7
  import types
7
8
  import urllib.parse
@@ -18,14 +19,13 @@ from typing import (
18
19
  )
19
20
 
20
21
  from reflex import constants
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
25
22
  from reflex.utils import format
26
23
  from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch
27
24
  from reflex.utils.types import ArgsSpec
28
- from reflex.vars import Var, VarData
25
+ from reflex.vars import VarData
26
+ from reflex.vars.base import LiteralVar, Var
27
+ from reflex.vars.function import FunctionStringVar, FunctionVar
28
+ from reflex.vars.object import ObjectVar
29
29
 
30
30
  try:
31
31
  from typing import Annotated
@@ -33,7 +33,11 @@ except ImportError:
33
33
  from typing_extensions import Annotated
34
34
 
35
35
 
36
- class Event(Base):
36
+ @dataclasses.dataclass(
37
+ init=True,
38
+ frozen=True,
39
+ )
40
+ class Event:
37
41
  """An event that describes any state change in the app."""
38
42
 
39
43
  # The token to specify the client that the event is for.
@@ -43,10 +47,10 @@ class Event(Base):
43
47
  name: str
44
48
 
45
49
  # The routing data where event occurred
46
- router_data: Dict[str, Any] = {}
50
+ router_data: Dict[str, Any] = dataclasses.field(default_factory=dict)
47
51
 
48
52
  # The event payload.
49
- payload: Dict[str, Any] = {}
53
+ payload: Dict[str, Any] = dataclasses.field(default_factory=dict)
50
54
 
51
55
  @property
52
56
  def substate_token(self) -> str:
@@ -81,11 +85,15 @@ def background(fn):
81
85
  return fn
82
86
 
83
87
 
84
- class EventActionsMixin(Base):
88
+ @dataclasses.dataclass(
89
+ init=True,
90
+ frozen=True,
91
+ )
92
+ class EventActionsMixin:
85
93
  """Mixin for DOM event actions."""
86
94
 
87
95
  # Whether to `preventDefault` or `stopPropagation` on the event.
88
- event_actions: Dict[str, Union[bool, int]] = {}
96
+ event_actions: Dict[str, Union[bool, int]] = dataclasses.field(default_factory=dict)
89
97
 
90
98
  @property
91
99
  def stop_propagation(self):
@@ -94,8 +102,9 @@ class EventActionsMixin(Base):
94
102
  Returns:
95
103
  New EventHandler-like with stopPropagation set to True.
96
104
  """
97
- return self.copy(
98
- update={"event_actions": {"stopPropagation": True, **self.event_actions}},
105
+ return dataclasses.replace(
106
+ self,
107
+ event_actions={"stopPropagation": True, **self.event_actions},
99
108
  )
100
109
 
101
110
  @property
@@ -105,8 +114,9 @@ class EventActionsMixin(Base):
105
114
  Returns:
106
115
  New EventHandler-like with preventDefault set to True.
107
116
  """
108
- return self.copy(
109
- update={"event_actions": {"preventDefault": True, **self.event_actions}},
117
+ return dataclasses.replace(
118
+ self,
119
+ event_actions={"preventDefault": True, **self.event_actions},
110
120
  )
111
121
 
112
122
  def throttle(self, limit_ms: int):
@@ -118,8 +128,9 @@ class EventActionsMixin(Base):
118
128
  Returns:
119
129
  New EventHandler-like with throttle set to limit_ms.
120
130
  """
121
- return self.copy(
122
- update={"event_actions": {"throttle": limit_ms, **self.event_actions}},
131
+ return dataclasses.replace(
132
+ self,
133
+ event_actions={"throttle": limit_ms, **self.event_actions},
123
134
  )
124
135
 
125
136
  def debounce(self, delay_ms: int):
@@ -131,26 +142,25 @@ class EventActionsMixin(Base):
131
142
  Returns:
132
143
  New EventHandler-like with debounce set to delay_ms.
133
144
  """
134
- return self.copy(
135
- update={"event_actions": {"debounce": delay_ms, **self.event_actions}},
145
+ return dataclasses.replace(
146
+ self,
147
+ event_actions={"debounce": delay_ms, **self.event_actions},
136
148
  )
137
149
 
138
150
 
151
+ @dataclasses.dataclass(
152
+ init=True,
153
+ frozen=True,
154
+ )
139
155
  class EventHandler(EventActionsMixin):
140
156
  """An event handler responds to an event to update the state."""
141
157
 
142
158
  # The function to call in response to the event.
143
- fn: Any
159
+ fn: Any = dataclasses.field(default=None)
144
160
 
145
161
  # The full name of the state class this event handler is attached to.
146
162
  # Empty string means this event handler is a server side event.
147
- state_full_name: str = ""
148
-
149
- class Config:
150
- """The Pydantic config."""
151
-
152
- # Needed to allow serialization of Callable.
153
- frozen = True
163
+ state_full_name: str = dataclasses.field(default="")
154
164
 
155
165
  @classmethod
156
166
  def __class_getitem__(cls, args_spec: str) -> Annotated:
@@ -191,7 +201,7 @@ class EventHandler(EventActionsMixin):
191
201
 
192
202
  # Get the function args.
193
203
  fn_args = inspect.getfullargspec(self.fn).args[1:]
194
- fn_args = (ImmutableVar.create_safe(arg) for arg in fn_args)
204
+ fn_args = (Var(_js_expr=arg) for arg in fn_args)
195
205
 
196
206
  # Construct the payload.
197
207
  values = []
@@ -215,6 +225,10 @@ class EventHandler(EventActionsMixin):
215
225
  )
216
226
 
217
227
 
228
+ @dataclasses.dataclass(
229
+ init=True,
230
+ frozen=True,
231
+ )
218
232
  class EventSpec(EventActionsMixin):
219
233
  """An event specification.
220
234
 
@@ -223,23 +237,37 @@ class EventSpec(EventActionsMixin):
223
237
  """
224
238
 
225
239
  # The event handler.
226
- handler: EventHandler
240
+ handler: EventHandler = dataclasses.field(default=None) # type: ignore
227
241
 
228
242
  # The handler on the client to process event.
229
- client_handler_name: str = ""
243
+ client_handler_name: str = dataclasses.field(default="")
230
244
 
231
245
  # The arguments to pass to the function.
232
- args: Tuple[Tuple[ImmutableVar, ImmutableVar], ...] = ()
246
+ args: Tuple[Tuple[Var, Var], ...] = dataclasses.field(default_factory=tuple)
233
247
 
234
- class Config:
235
- """The Pydantic config."""
248
+ def __init__(
249
+ self,
250
+ handler: EventHandler,
251
+ event_actions: Dict[str, Union[bool, int]] | None = None,
252
+ client_handler_name: str = "",
253
+ args: Tuple[Tuple[Var, Var], ...] = tuple(),
254
+ ):
255
+ """Initialize an EventSpec.
236
256
 
237
- # Required to allow tuple fields.
238
- frozen = True
239
-
240
- def with_args(
241
- self, args: Tuple[Tuple[ImmutableVar, ImmutableVar], ...]
242
- ) -> EventSpec:
257
+ Args:
258
+ event_actions: The event actions.
259
+ handler: The event handler.
260
+ client_handler_name: The client handler name.
261
+ args: The arguments to pass to the function.
262
+ """
263
+ if event_actions is None:
264
+ event_actions = {}
265
+ object.__setattr__(self, "event_actions", event_actions)
266
+ object.__setattr__(self, "handler", handler)
267
+ object.__setattr__(self, "client_handler_name", client_handler_name)
268
+ object.__setattr__(self, "args", args or tuple())
269
+
270
+ def with_args(self, args: Tuple[Tuple[Var, Var], ...]) -> EventSpec:
243
271
  """Copy the event spec, with updated args.
244
272
 
245
273
  Args:
@@ -255,7 +283,7 @@ class EventSpec(EventActionsMixin):
255
283
  event_actions=self.event_actions.copy(),
256
284
  )
257
285
 
258
- def add_args(self, *args: ImmutableVar) -> EventSpec:
286
+ def add_args(self, *args: Var) -> EventSpec:
259
287
  """Add arguments to the event spec.
260
288
 
261
289
  Args:
@@ -271,7 +299,7 @@ class EventSpec(EventActionsMixin):
271
299
 
272
300
  # Get the remaining unfilled function args.
273
301
  fn_args = inspect.getfullargspec(self.handler.fn).args[1 + len(self.args) :]
274
- fn_args = (ImmutableVar.create_safe(arg) for arg in fn_args)
302
+ fn_args = (Var(_js_expr=arg) for arg in fn_args)
275
303
 
276
304
  # Construct the payload.
277
305
  values = []
@@ -286,6 +314,9 @@ class EventSpec(EventActionsMixin):
286
314
  return self.with_args(self.args + new_payload)
287
315
 
288
316
 
317
+ @dataclasses.dataclass(
318
+ frozen=True,
319
+ )
289
320
  class CallableEventSpec(EventSpec):
290
321
  """Decorate an EventSpec-returning function to act as both a EventSpec and a function.
291
322
 
@@ -305,10 +336,13 @@ class CallableEventSpec(EventSpec):
305
336
  if fn is not None:
306
337
  default_event_spec = fn()
307
338
  super().__init__(
308
- fn=fn, # type: ignore
309
- **default_event_spec.dict(),
339
+ event_actions=default_event_spec.event_actions,
340
+ client_handler_name=default_event_spec.client_handler_name,
341
+ args=default_event_spec.args,
342
+ handler=default_event_spec.handler,
310
343
  **kwargs,
311
344
  )
345
+ object.__setattr__(self, "fn", fn)
312
346
  else:
313
347
  super().__init__(**kwargs)
314
348
 
@@ -332,12 +366,16 @@ class CallableEventSpec(EventSpec):
332
366
  return self.fn(*args, **kwargs)
333
367
 
334
368
 
369
+ @dataclasses.dataclass(
370
+ init=True,
371
+ frozen=True,
372
+ )
335
373
  class EventChain(EventActionsMixin):
336
374
  """Container for a chain of events that will be executed in order."""
337
375
 
338
- events: List[EventSpec]
376
+ events: List[EventSpec] = dataclasses.field(default_factory=list)
339
377
 
340
- args_spec: Optional[Callable]
378
+ args_spec: Optional[Callable] = dataclasses.field(default=None)
341
379
 
342
380
 
343
381
  # These chains can be used for their side effects when no other events are desired.
@@ -345,14 +383,22 @@ stop_propagation = EventChain(events=[], args_spec=lambda: []).stop_propagation
345
383
  prevent_default = EventChain(events=[], args_spec=lambda: []).prevent_default
346
384
 
347
385
 
348
- class Target(Base):
386
+ @dataclasses.dataclass(
387
+ init=True,
388
+ frozen=True,
389
+ )
390
+ class Target:
349
391
  """A Javascript event target."""
350
392
 
351
393
  checked: bool = False
352
394
  value: Any = None
353
395
 
354
396
 
355
- class FrontendEvent(Base):
397
+ @dataclasses.dataclass(
398
+ init=True,
399
+ frozen=True,
400
+ )
401
+ class FrontendEvent:
356
402
  """A Javascript event."""
357
403
 
358
404
  target: Target = Target()
@@ -360,7 +406,11 @@ class FrontendEvent(Base):
360
406
  value: Any = None
361
407
 
362
408
 
363
- class FileUpload(Base):
409
+ @dataclasses.dataclass(
410
+ init=True,
411
+ frozen=True,
412
+ )
413
+ class FileUpload:
364
414
  """Class to represent a file upload."""
365
415
 
366
416
  upload_id: Optional[str] = None
@@ -395,15 +445,15 @@ class FileUpload(Base):
395
445
  upload_id = self.upload_id or DEFAULT_UPLOAD_ID
396
446
  spec_args = [
397
447
  (
398
- ImmutableVar.create_safe("files"),
399
- ImmutableVar(
400
- _var_name="filesById",
448
+ Var(_js_expr="files"),
449
+ Var(
450
+ _js_expr="filesById",
401
451
  _var_type=Dict[str, Any],
402
452
  _var_data=VarData.merge(upload_files_context_var_data),
403
453
  ).to(ObjectVar)[LiteralVar.create(upload_id)],
404
454
  ),
405
455
  (
406
- ImmutableVar.create_safe("upload_id"),
456
+ Var(_js_expr="upload_id"),
407
457
  LiteralVar.create(upload_id),
408
458
  ),
409
459
  ]
@@ -423,7 +473,7 @@ class FileUpload(Base):
423
473
  ) # type: ignore
424
474
  else:
425
475
  raise ValueError(f"{on_upload_progress} is not a valid event handler.")
426
- if isinstance(events, ImmutableVar):
476
+ if isinstance(events, Var):
427
477
  raise ValueError(f"{on_upload_progress} cannot return a var {events}.")
428
478
  on_upload_progress_chain = EventChain(
429
479
  events=events,
@@ -432,7 +482,7 @@ class FileUpload(Base):
432
482
  formatted_chain = str(format.format_prop(on_upload_progress_chain))
433
483
  spec_args.append(
434
484
  (
435
- ImmutableVar.create_safe("on_upload_progress"),
485
+ Var(_js_expr="on_upload_progress"),
436
486
  FunctionStringVar(
437
487
  formatted_chain.strip("{}"),
438
488
  ).to(FunctionVar, EventChain),
@@ -472,7 +522,7 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
472
522
  handler=EventHandler(fn=fn),
473
523
  args=tuple(
474
524
  (
475
- ImmutableVar.create_safe(k),
525
+ Var(_js_expr=k),
476
526
  LiteralVar.create(v),
477
527
  )
478
528
  for k, v in kwargs.items()
@@ -678,9 +728,9 @@ def set_clipboard(content: str) -> EventSpec:
678
728
 
679
729
 
680
730
  def download(
681
- url: str | ImmutableVar | None = None,
682
- filename: Optional[str | ImmutableVar] = None,
683
- data: str | bytes | ImmutableVar | None = None,
731
+ url: str | Var | None = None,
732
+ filename: Optional[str | Var] = None,
733
+ data: str | bytes | Var | None = None,
684
734
  ) -> EventSpec:
685
735
  """Download the file at a given path or with the specified data.
686
736
 
@@ -716,7 +766,7 @@ def download(
716
766
  if isinstance(data, str):
717
767
  # Caller provided a plain text string to download.
718
768
  url = "data:text/plain," + urllib.parse.quote(data)
719
- elif isinstance(data, ImmutableVar):
769
+ elif isinstance(data, Var):
720
770
  # Need to check on the frontend if the Var already looks like a data: URI.
721
771
 
722
772
  is_data_url = (data.js_type() == "string") & (
@@ -873,15 +923,13 @@ def parse_args_spec(arg_spec: ArgsSpec):
873
923
  annotations = get_type_hints(arg_spec)
874
924
  return arg_spec(
875
925
  *[
876
- ImmutableVar(f"_{l_arg}").to(
877
- ObjectVar, annotations.get(l_arg, FrontendEvent)
878
- )
926
+ Var(f"_{l_arg}").to(annotations.get(l_arg, FrontendEvent))
879
927
  for l_arg in spec.args
880
928
  ]
881
929
  )
882
930
 
883
931
 
884
- def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | ImmutableVar:
932
+ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
885
933
  """Call a function to a list of event specs.
886
934
 
887
935
  The function should return a single EventSpec, a list of EventSpecs, or a
@@ -922,7 +970,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Immutab
922
970
  out = fn(*parsed_args)
923
971
 
924
972
  # If the function returns a Var, assume it's an EventChain and render it directly.
925
- if isinstance(out, ImmutableVar):
973
+ if isinstance(out, Var):
926
974
  return out
927
975
 
928
976
  # Convert the output to a list.
@@ -951,7 +999,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Immutab
951
999
 
952
1000
  def get_handler_args(
953
1001
  event_spec: EventSpec,
954
- ) -> tuple[tuple[ImmutableVar, ImmutableVar], ...]:
1002
+ ) -> tuple[tuple[Var, Var], ...]:
955
1003
  """Get the handler args for the given event spec.
956
1004
 
957
1005
  Args:
@@ -1002,7 +1050,7 @@ def fix_events(
1002
1050
  e = e()
1003
1051
  assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
1004
1052
  name = format.format_event_handler(e.handler)
1005
- payload = {k._var_name: v._decode() for k, v in e.args} # type: ignore
1053
+ payload = {k._js_expr: v._decode() for k, v in e.args} # type: ignore
1006
1054
 
1007
1055
  # Filter router_data to reduce payload size
1008
1056
  event_router_data = {