reflex 0.5.5a2__py3-none-any.whl → 0.5.6a1__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 (262) hide show
  1. reflex/.templates/jinja/web/pages/_app.js.jinja2 +0 -1
  2. reflex/.templates/jinja/web/utils/context.js.jinja2 +2 -0
  3. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +20 -2
  4. reflex/.templates/web/utils/helpers/paste.js +59 -0
  5. reflex/__init__.py +2 -0
  6. reflex/__init__.pyi +86 -87
  7. reflex/app.py +64 -126
  8. reflex/app_mixins/__init__.py +5 -0
  9. reflex/app_mixins/lifespan.py +57 -0
  10. reflex/app_mixins/middleware.py +93 -0
  11. reflex/app_mixins/mixin.py +14 -0
  12. reflex/compiler/compiler.py +6 -1
  13. reflex/components/__init__.pyi +0 -2
  14. reflex/components/base/__init__.pyi +1 -3
  15. reflex/components/base/app_wrap.pyi +21 -22
  16. reflex/components/base/body.pyi +21 -20
  17. reflex/components/base/document.pyi +85 -85
  18. reflex/components/base/fragment.pyi +21 -20
  19. reflex/components/base/head.pyi +37 -36
  20. reflex/components/base/link.pyi +37 -37
  21. reflex/components/base/meta.pyi +69 -70
  22. reflex/components/base/script.py +6 -2
  23. reflex/components/base/script.pyi +31 -27
  24. reflex/components/chakra/base.pyi +54 -56
  25. reflex/components/chakra/datadisplay/badge.pyi +21 -21
  26. reflex/components/chakra/datadisplay/code.pyi +21 -20
  27. reflex/components/chakra/datadisplay/divider.pyi +21 -22
  28. reflex/components/chakra/datadisplay/keyboard_key.pyi +21 -20
  29. reflex/components/chakra/datadisplay/list.pyi +69 -71
  30. reflex/components/chakra/datadisplay/stat.pyi +101 -102
  31. reflex/components/chakra/datadisplay/table.pyi +149 -153
  32. reflex/components/chakra/datadisplay/tag.pyi +85 -89
  33. reflex/components/chakra/disclosure/accordion.pyi +87 -93
  34. reflex/components/chakra/disclosure/tabs.pyi +85 -90
  35. reflex/components/chakra/disclosure/transition.pyi +104 -105
  36. reflex/components/chakra/disclosure/visuallyhidden.pyi +21 -20
  37. reflex/components/chakra/feedback/alert.pyi +69 -70
  38. reflex/components/chakra/feedback/circularprogress.pyi +38 -40
  39. reflex/components/chakra/feedback/progress.pyi +22 -23
  40. reflex/components/chakra/feedback/skeleton.pyi +53 -53
  41. reflex/components/chakra/feedback/spinner.pyi +21 -21
  42. reflex/components/chakra/forms/button.pyi +37 -42
  43. reflex/components/chakra/forms/checkbox.pyi +38 -39
  44. reflex/components/chakra/forms/colormodeswitch.pyi +72 -71
  45. reflex/components/chakra/forms/date_picker.pyi +24 -24
  46. reflex/components/chakra/forms/date_time_picker.pyi +24 -24
  47. reflex/components/chakra/forms/editable.pyi +73 -74
  48. reflex/components/chakra/forms/email.pyi +24 -24
  49. reflex/components/chakra/forms/form.pyi +112 -141
  50. reflex/components/chakra/forms/iconbutton.pyi +21 -22
  51. reflex/components/chakra/forms/input.pyi +104 -111
  52. reflex/components/chakra/forms/numberinput.pyi +87 -91
  53. reflex/components/chakra/forms/password.pyi +24 -24
  54. reflex/components/chakra/forms/pininput.pyi +39 -45
  55. reflex/components/chakra/forms/radio.pyi +38 -43
  56. reflex/components/chakra/forms/rangeslider.pyi +72 -76
  57. reflex/components/chakra/forms/select.pyi +39 -44
  58. reflex/components/chakra/forms/slider.pyi +88 -91
  59. reflex/components/chakra/forms/switch.pyi +22 -23
  60. reflex/components/chakra/forms/textarea.pyi +24 -27
  61. reflex/components/chakra/forms/time_picker.pyi +24 -24
  62. reflex/components/chakra/layout/aspect_ratio.pyi +21 -21
  63. reflex/components/chakra/layout/box.pyi +21 -22
  64. reflex/components/chakra/layout/card.pyi +69 -73
  65. reflex/components/chakra/layout/center.pyi +53 -52
  66. reflex/components/chakra/layout/container.pyi +21 -21
  67. reflex/components/chakra/layout/flex.pyi +23 -26
  68. reflex/components/chakra/layout/grid.pyi +53 -54
  69. reflex/components/chakra/layout/spacer.pyi +21 -20
  70. reflex/components/chakra/layout/stack.pyi +62 -60
  71. reflex/components/chakra/layout/wrap.pyi +37 -38
  72. reflex/components/chakra/media/avatar.pyi +54 -55
  73. reflex/components/chakra/media/icon.pyi +37 -38
  74. reflex/components/chakra/media/image.pyi +24 -26
  75. reflex/components/chakra/navigation/breadcrumb.pyi +69 -71
  76. reflex/components/chakra/navigation/link.pyi +20 -21
  77. reflex/components/chakra/navigation/linkoverlay.pyi +37 -37
  78. reflex/components/chakra/navigation/stepper.pyi +149 -151
  79. reflex/components/chakra/overlay/alertdialog.pyi +121 -124
  80. reflex/components/chakra/overlay/drawer.pyi +121 -126
  81. reflex/components/chakra/overlay/menu.pyi +135 -141
  82. reflex/components/chakra/overlay/modal.pyi +121 -124
  83. reflex/components/chakra/overlay/popover.pyi +151 -156
  84. reflex/components/chakra/overlay/tooltip.pyi +23 -24
  85. reflex/components/chakra/typography/heading.pyi +21 -21
  86. reflex/components/chakra/typography/highlight.pyi +21 -23
  87. reflex/components/chakra/typography/span.pyi +21 -21
  88. reflex/components/chakra/typography/text.pyi +21 -21
  89. reflex/components/component.py +6 -2
  90. reflex/components/core/__init__.py +2 -0
  91. reflex/components/core/__init__.pyi +9 -7
  92. reflex/components/core/banner.pyi +118 -146
  93. reflex/components/core/breakpoints.py +95 -0
  94. reflex/components/core/client_side_routing.pyi +37 -39
  95. reflex/components/core/clipboard.py +95 -0
  96. reflex/components/core/clipboard.pyi +102 -0
  97. reflex/components/core/debounce.pyi +23 -28
  98. reflex/components/core/html.pyi +38 -55
  99. reflex/components/core/upload.pyi +74 -91
  100. reflex/components/datadisplay/__init__.pyi +2 -3
  101. reflex/components/datadisplay/code.py +3 -3
  102. reflex/components/datadisplay/code.pyi +22 -31
  103. reflex/components/datadisplay/dataeditor.pyi +41 -45
  104. reflex/components/el/__init__.pyi +131 -135
  105. reflex/components/el/element.pyi +21 -20
  106. reflex/components/el/elements/__init__.pyi +131 -132
  107. reflex/components/el/elements/base.pyi +38 -55
  108. reflex/components/el/elements/forms.pyi +558 -878
  109. reflex/components/el/elements/inline.pyi +941 -1403
  110. reflex/components/el/elements/media.pyi +645 -994
  111. reflex/components/el/elements/metadata.pyi +186 -268
  112. reflex/components/el/elements/other.pyi +239 -353
  113. reflex/components/el/elements/scripts.pyi +113 -171
  114. reflex/components/el/elements/sectioning.pyi +500 -739
  115. reflex/components/el/elements/tables.pyi +355 -551
  116. reflex/components/el/elements/typography.pyi +510 -760
  117. reflex/components/gridjs/datatable.pyi +38 -42
  118. reflex/components/lucide/icon.pyi +37 -38
  119. reflex/components/markdown/markdown.pyi +23 -36
  120. reflex/components/moment/moment.pyi +23 -25
  121. reflex/components/next/base.pyi +21 -20
  122. reflex/components/next/image.pyi +25 -27
  123. reflex/components/next/link.pyi +21 -21
  124. reflex/components/next/video.pyi +22 -22
  125. reflex/components/plotly/plotly.pyi +42 -45
  126. reflex/components/radix/__init__.pyi +26 -30
  127. reflex/components/radix/primitives/__init__.pyi +0 -2
  128. reflex/components/radix/primitives/accordion.pyi +119 -127
  129. reflex/components/radix/primitives/base.pyi +37 -40
  130. reflex/components/radix/primitives/drawer.pyi +175 -179
  131. reflex/components/radix/primitives/form.pyi +250 -336
  132. reflex/components/radix/primitives/progress.pyi +92 -96
  133. reflex/components/radix/primitives/slider.pyi +87 -89
  134. reflex/components/radix/themes/__init__.pyi +0 -2
  135. reflex/components/radix/themes/base.pyi +118 -121
  136. reflex/components/radix/themes/color_mode.pyi +103 -117
  137. reflex/components/radix/themes/components/__init__.pyi +12 -14
  138. reflex/components/radix/themes/components/alert_dialog.py +2 -1
  139. reflex/components/radix/themes/components/alert_dialog.pyi +150 -157
  140. reflex/components/radix/themes/components/aspect_ratio.pyi +22 -22
  141. reflex/components/radix/themes/components/avatar.py +2 -1
  142. reflex/components/radix/themes/components/avatar.pyi +32 -23
  143. reflex/components/radix/themes/components/badge.py +2 -1
  144. reflex/components/radix/themes/components/badge.pyi +50 -57
  145. reflex/components/radix/themes/components/button.py +2 -1
  146. reflex/components/radix/themes/components/button.pyi +60 -79
  147. reflex/components/radix/themes/components/callout.py +2 -1
  148. reflex/components/radix/themes/components/callout.pyi +201 -258
  149. reflex/components/radix/themes/components/card.py +2 -1
  150. reflex/components/radix/themes/components/card.pyi +48 -56
  151. reflex/components/radix/themes/components/checkbox.py +2 -1
  152. reflex/components/radix/themes/components/checkbox.pyi +68 -62
  153. reflex/components/radix/themes/components/checkbox_cards.py +8 -3
  154. reflex/components/radix/themes/components/checkbox_cards.pyi +87 -44
  155. reflex/components/radix/themes/components/checkbox_group.py +2 -1
  156. reflex/components/radix/themes/components/checkbox_group.pyi +49 -40
  157. reflex/components/radix/themes/components/context_menu.py +2 -1
  158. reflex/components/radix/themes/components/context_menu.pyi +153 -147
  159. reflex/components/radix/themes/components/data_list.py +8 -7
  160. reflex/components/radix/themes/components/data_list.pyi +116 -78
  161. reflex/components/radix/themes/components/dialog.py +2 -1
  162. reflex/components/radix/themes/components/dialog.pyi +154 -161
  163. reflex/components/radix/themes/components/dropdown_menu.py +2 -1
  164. reflex/components/radix/themes/components/dropdown_menu.pyi +169 -163
  165. reflex/components/radix/themes/components/hover_card.py +2 -1
  166. reflex/components/radix/themes/components/hover_card.pyi +97 -107
  167. reflex/components/radix/themes/components/icon_button.py +2 -1
  168. reflex/components/radix/themes/components/icon_button.pyi +59 -82
  169. reflex/components/radix/themes/components/inset.py +10 -9
  170. reflex/components/radix/themes/components/inset.pyi +109 -61
  171. reflex/components/radix/themes/components/popover.py +2 -1
  172. reflex/components/radix/themes/components/popover.pyi +105 -112
  173. reflex/components/radix/themes/components/progress.py +2 -1
  174. reflex/components/radix/themes/components/progress.pyi +32 -24
  175. reflex/components/radix/themes/components/radio.py +2 -1
  176. reflex/components/radix/themes/components/radio.pyi +32 -23
  177. reflex/components/radix/themes/components/radio_cards.py +51 -3
  178. reflex/components/radix/themes/components/radio_cards.pyi +120 -44
  179. reflex/components/radix/themes/components/radio_group.py +5 -2
  180. reflex/components/radix/themes/components/radio_group.pyi +82 -77
  181. reflex/components/radix/themes/components/scroll_area.pyi +21 -21
  182. reflex/components/radix/themes/components/segmented_control.py +2 -1
  183. reflex/components/radix/themes/components/segmented_control.pyi +52 -46
  184. reflex/components/radix/themes/components/select.py +2 -1
  185. reflex/components/radix/themes/components/select.pyi +188 -164
  186. reflex/components/radix/themes/components/separator.py +5 -2
  187. reflex/components/radix/themes/components/separator.pyi +40 -24
  188. reflex/components/radix/themes/components/skeleton.py +7 -6
  189. reflex/components/radix/themes/components/skeleton.pyi +40 -26
  190. reflex/components/radix/themes/components/slider.py +2 -1
  191. reflex/components/radix/themes/components/slider.pyi +40 -31
  192. reflex/components/radix/themes/components/spinner.py +2 -1
  193. reflex/components/radix/themes/components/spinner.pyi +31 -22
  194. reflex/components/radix/themes/components/switch.py +2 -1
  195. reflex/components/radix/themes/components/switch.pyi +33 -25
  196. reflex/components/radix/themes/components/table.py +2 -1
  197. reflex/components/radix/themes/components/table.pyi +265 -404
  198. reflex/components/radix/themes/components/tabs.py +14 -1
  199. reflex/components/radix/themes/components/tabs.pyi +113 -92
  200. reflex/components/radix/themes/components/text_area.py +3 -2
  201. reflex/components/radix/themes/components/text_area.pyi +64 -66
  202. reflex/components/radix/themes/components/text_field.py +2 -1
  203. reflex/components/radix/themes/components/text_field.pyi +116 -140
  204. reflex/components/radix/themes/components/tooltip.pyi +32 -37
  205. reflex/components/radix/themes/layout/__init__.pyi +4 -7
  206. reflex/components/radix/themes/layout/base.py +10 -9
  207. reflex/components/radix/themes/layout/base.pyi +121 -31
  208. reflex/components/radix/themes/layout/box.pyi +39 -53
  209. reflex/components/radix/themes/layout/center.pyi +89 -58
  210. reflex/components/radix/themes/layout/container.py +4 -1
  211. reflex/components/radix/themes/layout/container.pyi +51 -58
  212. reflex/components/radix/themes/layout/flex.py +6 -5
  213. reflex/components/radix/themes/layout/flex.pyi +91 -61
  214. reflex/components/radix/themes/layout/grid.py +9 -8
  215. reflex/components/radix/themes/layout/grid.pyi +116 -64
  216. reflex/components/radix/themes/layout/list.pyi +173 -233
  217. reflex/components/radix/themes/layout/section.py +4 -1
  218. reflex/components/radix/themes/layout/section.pyi +50 -57
  219. reflex/components/radix/themes/layout/spacer.pyi +89 -58
  220. reflex/components/radix/themes/layout/stack.pyi +160 -160
  221. reflex/components/radix/themes/typography/__init__.pyi +0 -2
  222. reflex/components/radix/themes/typography/blockquote.py +3 -2
  223. reflex/components/radix/themes/typography/blockquote.pyi +58 -59
  224. reflex/components/radix/themes/typography/code.py +3 -2
  225. reflex/components/radix/themes/typography/code.pyi +57 -58
  226. reflex/components/radix/themes/typography/heading.py +5 -4
  227. reflex/components/radix/themes/typography/heading.pyi +71 -60
  228. reflex/components/radix/themes/typography/link.py +4 -3
  229. reflex/components/radix/themes/typography/link.pyi +74 -82
  230. reflex/components/radix/themes/typography/text.py +5 -4
  231. reflex/components/radix/themes/typography/text.pyi +330 -364
  232. reflex/components/react_player/audio.pyi +37 -36
  233. reflex/components/react_player/react_player.pyi +37 -38
  234. reflex/components/react_player/video.pyi +37 -36
  235. reflex/components/recharts/__init__.pyi +41 -42
  236. reflex/components/recharts/cartesian.pyi +384 -400
  237. reflex/components/recharts/charts.pyi +224 -231
  238. reflex/components/recharts/general.pyi +89 -96
  239. reflex/components/recharts/polar.pyi +97 -104
  240. reflex/components/recharts/recharts.pyi +37 -37
  241. reflex/components/sonner/toast.pyi +22 -27
  242. reflex/components/suneditor/editor.pyi +53 -58
  243. reflex/config.py +6 -0
  244. reflex/constants/event.py +1 -0
  245. reflex/experimental/layout.pyi +140 -194
  246. reflex/model.py +14 -2
  247. reflex/state.py +38 -45
  248. reflex/style.py +24 -13
  249. reflex/utils/codespaces.py +94 -0
  250. reflex/utils/compat.py +21 -0
  251. reflex/utils/exceptions.py +4 -0
  252. reflex/utils/format.py +25 -4
  253. reflex/utils/prerequisites.py +0 -13
  254. reflex/utils/pyi_generator.py +88 -61
  255. reflex/utils/types.py +83 -5
  256. reflex/vars.py +62 -5
  257. reflex/vars.pyi +23 -11
  258. {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/METADATA +4 -5
  259. {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/RECORD +262 -253
  260. {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/LICENSE +0 -0
  261. {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/WHEEL +0 -0
  262. {reflex-0.5.5a2.dist-info → reflex-0.5.6a1.dist-info}/entry_points.txt +0 -0
reflex/state.py CHANGED
@@ -40,6 +40,7 @@ from redis.exceptions import ResponseError
40
40
 
41
41
  from reflex import constants
42
42
  from reflex.base import Base
43
+ from reflex.config import get_config
43
44
  from reflex.event import (
44
45
  BACKGROUND_TASK_MARKER,
45
46
  Event,
@@ -60,6 +61,7 @@ if TYPE_CHECKING:
60
61
 
61
62
  Delta = Dict[str, Any]
62
63
  var = computed_var
64
+ config = get_config()
63
65
 
64
66
 
65
67
  # If the state is this large, it's considered a performance issue.
@@ -197,16 +199,6 @@ def _no_chain_background_task(
197
199
  raise TypeError(f"{fn} is marked as a background task, but is not async.")
198
200
 
199
201
 
200
- RESERVED_BACKEND_VAR_NAMES = {
201
- "_backend_vars",
202
- "_computed_var_dependencies",
203
- "_substate_var_dependencies",
204
- "_always_dirty_computed_vars",
205
- "_always_dirty_substates",
206
- "_was_touched",
207
- }
208
-
209
-
210
202
  def _substate_key(
211
203
  token: str,
212
204
  state_cls_or_name: BaseState | Type[BaseState] | str | list[str],
@@ -313,10 +305,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
313
305
  # Vars inherited by the parent state.
314
306
  inherited_vars: ClassVar[Dict[str, Var]] = {}
315
307
 
316
- # Backend vars that are never sent to the client.
308
+ # Backend base vars that are never sent to the client.
317
309
  backend_vars: ClassVar[Dict[str, Any]] = {}
318
310
 
319
- # Backend vars inherited
311
+ # Backend base vars inherited
320
312
  inherited_backend_vars: ClassVar[Dict[str, Any]] = {}
321
313
 
322
314
  # The event handlers.
@@ -352,7 +344,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
352
344
  # The routing path that triggered the state
353
345
  router_data: Dict[str, Any] = {}
354
346
 
355
- # Per-instance copy of backend variable values
347
+ # Per-instance copy of backend base variable values
356
348
  _backend_vars: Dict[str, Any] = {}
357
349
 
358
350
  # The router data for the current page
@@ -409,11 +401,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
409
401
 
410
402
  # Create a fresh copy of the backend variables for this instance
411
403
  self._backend_vars = copy.deepcopy(
412
- {
413
- name: item
414
- for name, item in self.backend_vars.items()
415
- if name not in self.computed_vars
416
- }
404
+ {name: item for name, item in self.backend_vars.items()}
417
405
  )
418
406
 
419
407
  def __repr__(self) -> str:
@@ -500,25 +488,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
500
488
  new_backend_vars = {
501
489
  name: value
502
490
  for name, value in cls.__dict__.items()
503
- if types.is_backend_variable(name, cls)
504
- and name not in RESERVED_BACKEND_VAR_NAMES
505
- and name not in cls.inherited_backend_vars
506
- and not isinstance(value, FunctionType)
507
- and not isinstance(value, ComputedVar)
508
- }
509
-
510
- # Get backend computed vars
511
- backend_computed_vars = {
512
- v._var_name: v._var_set_state(cls)
513
- for v in computed_vars
514
- if types.is_backend_variable(v._var_name, cls)
515
- and v._var_name not in cls.inherited_backend_vars
491
+ if types.is_backend_base_variable(name, cls)
516
492
  }
517
493
 
518
494
  cls.backend_vars = {
519
495
  **cls.inherited_backend_vars,
520
496
  **new_backend_vars,
521
- **backend_computed_vars,
522
497
  }
523
498
 
524
499
  # Set the base and computed vars.
@@ -560,6 +535,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
560
535
  cls.computed_vars[newcv._var_name] = newcv
561
536
  cls.vars[newcv._var_name] = newcv
562
537
  continue
538
+ if types.is_backend_base_variable(name, mixin):
539
+ cls.backend_vars[name] = copy.deepcopy(value)
540
+ continue
563
541
  if events.get(name) is not None:
564
542
  continue
565
543
  if not cls._item_is_event_handler(name, value):
@@ -743,7 +721,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
743
721
  "dirty_substates",
744
722
  "router_data",
745
723
  }
746
- | RESERVED_BACKEND_VAR_NAMES
724
+ | types.RESERVED_BACKEND_VAR_NAMES
747
725
  )
748
726
 
749
727
  @classmethod
@@ -1096,10 +1074,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1096
1074
  setattr(self.parent_state, name, value)
1097
1075
  return
1098
1076
 
1099
- if (
1100
- types.is_backend_variable(name, self.__class__)
1101
- and name not in RESERVED_BACKEND_VAR_NAMES
1102
- ):
1077
+ if name in self.backend_vars:
1103
1078
  self._backend_vars.__setitem__(name, value)
1104
1079
  self.dirty_vars.add(name)
1105
1080
  self._mark_dirty()
@@ -1550,11 +1525,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1550
1525
  if self.computed_vars[cvar].needs_update(instance=self)
1551
1526
  )
1552
1527
 
1553
- def _dirty_computed_vars(self, from_vars: set[str] | None = None) -> set[str]:
1528
+ def _dirty_computed_vars(
1529
+ self, from_vars: set[str] | None = None, include_backend: bool = True
1530
+ ) -> set[str]:
1554
1531
  """Determine ComputedVars that need to be recalculated based on the given vars.
1555
1532
 
1556
1533
  Args:
1557
1534
  from_vars: find ComputedVar that depend on this set of vars. If unspecified, will use the dirty_vars.
1535
+ include_backend: whether to include backend vars in the calculation.
1558
1536
 
1559
1537
  Returns:
1560
1538
  Set of computed vars to include in the delta.
@@ -1563,6 +1541,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1563
1541
  cvar
1564
1542
  for dirty_var in from_vars or self.dirty_vars
1565
1543
  for cvar in self._computed_var_dependencies[dirty_var]
1544
+ if include_backend or not self.computed_vars[cvar]._backend
1566
1545
  )
1567
1546
 
1568
1547
  @classmethod
@@ -1598,19 +1577,23 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1598
1577
  self.dirty_vars.update(self._always_dirty_computed_vars)
1599
1578
  self._mark_dirty()
1600
1579
 
1580
+ frontend_computed_vars: set[str] = {
1581
+ name for name, cv in self.computed_vars.items() if not cv._backend
1582
+ }
1583
+
1601
1584
  # Return the dirty vars for this instance, any cached/dependent computed vars,
1602
1585
  # and always dirty computed vars (cache=False)
1603
1586
  delta_vars = (
1604
1587
  self.dirty_vars.intersection(self.base_vars)
1605
- .union(self.dirty_vars.intersection(self.computed_vars))
1606
- .union(self._dirty_computed_vars())
1588
+ .union(self.dirty_vars.intersection(frontend_computed_vars))
1589
+ .union(self._dirty_computed_vars(include_backend=False))
1607
1590
  .union(self._always_dirty_computed_vars)
1608
1591
  )
1609
1592
 
1610
1593
  subdelta = {
1611
1594
  prop: getattr(self, prop)
1612
1595
  for prop in delta_vars
1613
- if not types.is_backend_variable(prop, self.__class__)
1596
+ if not types.is_backend_base_variable(prop, type(self))
1614
1597
  }
1615
1598
  if len(subdelta) > 0:
1616
1599
  delta[self.get_full_name()] = subdelta
@@ -1739,12 +1722,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1739
1722
  else self.get_value(getattr(self, prop_name))
1740
1723
  )
1741
1724
  for prop_name, cv in self.computed_vars.items()
1725
+ if not cv._backend
1742
1726
  }
1743
1727
  elif include_computed:
1744
1728
  computed_vars = {
1745
1729
  # Include the computed vars.
1746
1730
  prop_name: self.get_value(getattr(self, prop_name))
1747
- for prop_name in self.computed_vars
1731
+ for prop_name, cv in self.computed_vars.items()
1732
+ if not cv._backend
1748
1733
  }
1749
1734
  else:
1750
1735
  computed_vars = {}
@@ -1757,6 +1742,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1757
1742
  for v in self.substates.values()
1758
1743
  ]:
1759
1744
  d.update(substate_d)
1745
+
1760
1746
  return d
1761
1747
 
1762
1748
  async def __aenter__(self) -> BaseState:
@@ -2202,7 +2188,14 @@ class StateManager(Base, ABC):
2202
2188
  """
2203
2189
  redis = prerequisites.get_redis()
2204
2190
  if redis is not None:
2205
- return StateManagerRedis(state=state, redis=redis)
2191
+ # make sure expiration values are obtained only from the config object on creation
2192
+ config = get_config()
2193
+ return StateManagerRedis(
2194
+ state=state,
2195
+ redis=redis,
2196
+ token_expiration=config.redis_token_expiration,
2197
+ lock_expiration=config.redis_lock_expiration,
2198
+ )
2206
2199
  return StateManagerMemory(state=state)
2207
2200
 
2208
2201
  @abstractmethod
@@ -2333,10 +2326,10 @@ class StateManagerRedis(StateManager):
2333
2326
  redis: Redis
2334
2327
 
2335
2328
  # The token expiration time (s).
2336
- token_expiration: int = constants.Expiration.TOKEN
2329
+ token_expiration: int = config.redis_token_expiration
2337
2330
 
2338
2331
  # The maximum time to hold a lock (ms).
2339
- lock_expiration: int = constants.Expiration.LOCK
2332
+ lock_expiration: int = config.redis_lock_expiration
2340
2333
 
2341
2334
  # The keyspace subscription string when redis is waiting for lock to be released
2342
2335
  _redis_notify_keyspace_events: str = (
reflex/style.py CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from typing import Any, Literal, Tuple, Type
6
6
 
7
7
  from reflex import constants
8
+ from reflex.components.core.breakpoints import Breakpoints, breakpoints_values
8
9
  from reflex.event import EventChain
9
10
  from reflex.utils import format
10
11
  from reflex.utils.imports import ImportVar
@@ -86,8 +87,6 @@ toggle_color_mode = _color_mode_var(
86
87
  _var_type=EventChain,
87
88
  )
88
89
 
89
- breakpoints = ["0", "30em", "48em", "62em", "80em", "96em"]
90
-
91
90
  STYLE_PROP_SHORTHAND_MAPPING = {
92
91
  "paddingX": ("paddingInlineStart", "paddingInlineEnd"),
93
92
  "paddingY": ("paddingTop", "paddingBottom"),
@@ -100,16 +99,16 @@ STYLE_PROP_SHORTHAND_MAPPING = {
100
99
  }
101
100
 
102
101
 
103
- def media_query(breakpoint_index: int):
102
+ def media_query(breakpoint_expr: str):
104
103
  """Create a media query selector.
105
104
 
106
105
  Args:
107
- breakpoint_index: The index of the breakpoint to use.
106
+ breakpoint_expr: The CSS expression representing the breakpoint.
108
107
 
109
108
  Returns:
110
109
  The media query selector used as a key in emotion css dict.
111
110
  """
112
- return f"@media screen and (min-width: {breakpoints[breakpoint_index]})"
111
+ return f"@media screen and (min-width: {breakpoint_expr})"
113
112
 
114
113
 
115
114
  def convert_item(style_item: str | Var) -> tuple[str, VarData | None]:
@@ -189,6 +188,10 @@ def convert(style_dict):
189
188
  update_out_dict(return_val, keys)
190
189
  # Combine all the collected VarData instances.
191
190
  var_data = VarData.merge(var_data, new_var_data)
191
+
192
+ if isinstance(style_dict, Breakpoints):
193
+ out = Breakpoints(out).factorize()
194
+
192
195
  return out, var_data
193
196
 
194
197
 
@@ -295,14 +298,22 @@ def format_as_emotion(style_dict: dict[str, Any]) -> Style | None:
295
298
 
296
299
  for orig_key, value in style_dict.items():
297
300
  key = _format_emotion_style_pseudo_selector(orig_key)
298
- if isinstance(value, list):
299
- # Apply media queries from responsive value list.
300
- mbps = {
301
- media_query(bp): (
302
- bp_value if isinstance(bp_value, dict) else {key: bp_value}
303
- )
304
- for bp, bp_value in enumerate(value)
305
- }
301
+ if isinstance(value, (Breakpoints, list)):
302
+ if isinstance(value, Breakpoints):
303
+ mbps = {
304
+ media_query(bp): (
305
+ bp_value if isinstance(bp_value, dict) else {key: bp_value}
306
+ )
307
+ for bp, bp_value in value.items()
308
+ }
309
+ else:
310
+ # Apply media queries from responsive value list.
311
+ mbps = {
312
+ media_query([0, *breakpoints_values][bp]): (
313
+ bp_value if isinstance(bp_value, dict) else {key: bp_value}
314
+ )
315
+ for bp, bp_value in enumerate(value)
316
+ }
306
317
  if key.startswith("&:"):
307
318
  emotion_style[key] = mbps
308
319
  else:
@@ -0,0 +1,94 @@
1
+ """Utilities for working with Github Codespaces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ from fastapi.responses import HTMLResponse
8
+
9
+ from reflex.components.base.script import Script
10
+ from reflex.components.component import Component
11
+ from reflex.components.core.banner import has_connection_errors
12
+ from reflex.components.core.cond import cond
13
+ from reflex.constants import Endpoint
14
+
15
+ redirect_script = """
16
+ const thisUrl = new URL(window.location.href);
17
+ const params = new URLSearchParams(thisUrl.search)
18
+
19
+ function doRedirect(url) {
20
+ if (!window.sessionStorage.getItem("authenticated_github_codespaces")) {
21
+ const a = document.createElement("a");
22
+ if (params.has("redirect_to")) {
23
+ a.href = params.get("redirect_to")
24
+ } else if (!window.location.href.startsWith(url)) {
25
+ a.href = url + `?redirect_to=${window.location.href}`
26
+ } else {
27
+ return
28
+ }
29
+ a.hidden = true;
30
+ a.click();
31
+ a.remove();
32
+ window.sessionStorage.setItem("authenticated_github_codespaces", "true")
33
+ }
34
+ }
35
+ doRedirect("%s")
36
+ """ % Endpoint.AUTH_CODESPACE.get_url()
37
+
38
+
39
+ def codespaces_port_forwarding_domain() -> str | None:
40
+ """Get the domain for port forwarding in Github Codespaces.
41
+
42
+ Returns:
43
+ The domain for port forwarding in Github Codespaces, or None if not running in Codespaces.
44
+ """
45
+ GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN = os.getenv(
46
+ "GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"
47
+ )
48
+ return GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN
49
+
50
+
51
+ def is_running_in_codespaces() -> bool:
52
+ """Check if the app is running in Github Codespaces.
53
+
54
+ Returns:
55
+ True if running in Github Codespaces, False otherwise.
56
+ """
57
+ return codespaces_port_forwarding_domain() is not None
58
+
59
+
60
+ def codespaces_auto_redirect() -> list[Component]:
61
+ """Get the components for automatically redirecting back to the app after authenticating a codespace port forward.
62
+
63
+ Returns:
64
+ A list containing the conditional redirect component, or empty list.
65
+ """
66
+ if is_running_in_codespaces():
67
+ return [cond(has_connection_errors, Script.create(redirect_script))]
68
+ return []
69
+
70
+
71
+ async def auth_codespace() -> HTMLResponse:
72
+ """Page automatically redirecting back to the app after authenticating a codespace port forward.
73
+
74
+ Returns:
75
+ An HTML response with an embedded script to redirect back to the app.
76
+ """
77
+ return HTMLResponse(
78
+ """
79
+ <html>
80
+ <head>
81
+ <title>Reflex Github Codespace Forward Successfully Authenticated</title>
82
+ </head>
83
+ <body>
84
+ <center>
85
+ <h2>Successfully Authenticated</h2>
86
+ </center>
87
+ <script language="javascript">
88
+ %s
89
+ </script>
90
+ </body>
91
+ </html>
92
+ """
93
+ % redirect_script
94
+ )
reflex/utils/compat.py CHANGED
@@ -4,6 +4,27 @@ import contextlib
4
4
  import sys
5
5
 
6
6
 
7
+ async def windows_hot_reload_lifespan_hack():
8
+ """[REF-3164] A hack to fix hot reload on Windows.
9
+
10
+ Uvicorn has an issue stopping itself on Windows after detecting changes in
11
+ the filesystem.
12
+
13
+ This workaround repeatedly prints and flushes null characters to stderr,
14
+ which seems to allow the uvicorn server to exit when the CTRL-C signal is
15
+ sent from the reloader process.
16
+
17
+ Don't ask me why this works, I discovered it by accident - masenf.
18
+ """
19
+ import asyncio
20
+ import sys
21
+
22
+ while True:
23
+ sys.stderr.write("\0")
24
+ sys.stderr.flush()
25
+ await asyncio.sleep(0.5)
26
+
27
+
7
28
  @contextlib.contextmanager
8
29
  def pydantic_v1_patch():
9
30
  """A context manager that patches the Pydantic module to mimic v1 behaviour.
@@ -61,6 +61,10 @@ class VarOperationTypeError(ReflexError, TypeError):
61
61
  """Custom TypeError for when unsupported operations are performed on vars."""
62
62
 
63
63
 
64
+ class VarDependencyError(ReflexError, ValueError):
65
+ """Custom ValueError for when a var depends on a non-existent var."""
66
+
67
+
64
68
  class InvalidStylePropError(ReflexError, TypeError):
65
69
  """Custom Type Error when style props have invalid values."""
66
70
 
reflex/utils/format.py CHANGED
@@ -210,10 +210,31 @@ def _escape_js_string(string: str) -> str:
210
210
  Returns:
211
211
  The escaped string.
212
212
  """
213
- # Escape backticks.
214
- string = string.replace(r"\`", "`")
215
- string = string.replace("`", r"\`")
216
- return string
213
+
214
+ # TODO: we may need to re-vist this logic after new Var API is implemented.
215
+ def escape_outside_segments(segment):
216
+ """Escape backticks in segments outside of `${}`.
217
+
218
+ Args:
219
+ segment: The part of the string to escape.
220
+
221
+ Returns:
222
+ The escaped or unescaped segment.
223
+ """
224
+ if segment.startswith("${") and segment.endswith("}"):
225
+ # Return the `${}` segment unchanged
226
+ return segment
227
+ else:
228
+ # Escape backticks in the segment
229
+ segment = segment.replace(r"\`", "`")
230
+ segment = segment.replace("`", r"\`")
231
+ return segment
232
+
233
+ # Split the string into parts, keeping the `${}` segments
234
+ parts = re.split(r"(\$\{.*?\})", string)
235
+ escaped_parts = [escape_outside_segments(part) for part in parts]
236
+ escaped_string = "".join(escaped_parts)
237
+ return escaped_string
217
238
 
218
239
 
219
240
  def _wrap_js_string(string: str) -> str:
@@ -949,22 +949,9 @@ def needs_reinit(frontend: bool = True) -> bool:
949
949
  return True
950
950
 
951
951
  if constants.IS_WINDOWS:
952
- import uvicorn
953
-
954
- uvi_ver = uvicorn.__version__
955
952
  console.warn(
956
953
  """Windows Subsystem for Linux (WSL) is recommended for improving initial install times."""
957
954
  )
958
- if sys.version_info >= (3, 12):
959
- console.warn(
960
- "Python 3.12 on Windows has known issues with hot reload (reflex-dev/reflex#3536). "
961
- "Python 3.11 is recommended with this release of Reflex."
962
- )
963
-
964
- if sys.version_info < (3, 12) and uvi_ver != "0.20.0":
965
- console.warn(
966
- f"""On Python < 3.12, `uvicorn==0.20.0` is recommended for improved hot reload times. Found {uvi_ver} instead."""
967
- )
968
955
 
969
956
  if windows_check_onedrive_in_path():
970
957
  console.warn(