reflex 0.5.5a2__py3-none-any.whl → 0.5.6__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 (264) 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 +9 -1
  6. reflex/__init__.pyi +89 -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/foreach.py +3 -2
  99. reflex/components/core/html.pyi +38 -55
  100. reflex/components/core/upload.py +1 -1
  101. reflex/components/core/upload.pyi +74 -91
  102. reflex/components/datadisplay/__init__.pyi +2 -3
  103. reflex/components/datadisplay/code.py +3 -3
  104. reflex/components/datadisplay/code.pyi +22 -31
  105. reflex/components/datadisplay/dataeditor.pyi +41 -45
  106. reflex/components/el/__init__.pyi +131 -135
  107. reflex/components/el/element.pyi +21 -20
  108. reflex/components/el/elements/__init__.pyi +131 -132
  109. reflex/components/el/elements/base.pyi +38 -55
  110. reflex/components/el/elements/forms.pyi +558 -878
  111. reflex/components/el/elements/inline.pyi +941 -1403
  112. reflex/components/el/elements/media.pyi +645 -994
  113. reflex/components/el/elements/metadata.pyi +186 -268
  114. reflex/components/el/elements/other.pyi +239 -353
  115. reflex/components/el/elements/scripts.pyi +113 -171
  116. reflex/components/el/elements/sectioning.pyi +500 -739
  117. reflex/components/el/elements/tables.pyi +355 -551
  118. reflex/components/el/elements/typography.pyi +510 -760
  119. reflex/components/gridjs/datatable.pyi +38 -42
  120. reflex/components/lucide/icon.pyi +37 -38
  121. reflex/components/markdown/markdown.pyi +23 -36
  122. reflex/components/moment/moment.pyi +23 -25
  123. reflex/components/next/base.pyi +21 -20
  124. reflex/components/next/image.pyi +25 -27
  125. reflex/components/next/link.pyi +21 -21
  126. reflex/components/next/video.pyi +22 -22
  127. reflex/components/plotly/plotly.pyi +42 -45
  128. reflex/components/radix/__init__.pyi +26 -30
  129. reflex/components/radix/primitives/__init__.pyi +0 -2
  130. reflex/components/radix/primitives/accordion.pyi +119 -127
  131. reflex/components/radix/primitives/base.pyi +37 -40
  132. reflex/components/radix/primitives/drawer.pyi +175 -179
  133. reflex/components/radix/primitives/form.pyi +250 -336
  134. reflex/components/radix/primitives/progress.pyi +92 -96
  135. reflex/components/radix/primitives/slider.pyi +87 -89
  136. reflex/components/radix/themes/__init__.pyi +0 -2
  137. reflex/components/radix/themes/base.pyi +118 -121
  138. reflex/components/radix/themes/color_mode.pyi +103 -117
  139. reflex/components/radix/themes/components/__init__.pyi +12 -14
  140. reflex/components/radix/themes/components/alert_dialog.py +2 -1
  141. reflex/components/radix/themes/components/alert_dialog.pyi +150 -157
  142. reflex/components/radix/themes/components/aspect_ratio.pyi +22 -22
  143. reflex/components/radix/themes/components/avatar.py +2 -1
  144. reflex/components/radix/themes/components/avatar.pyi +32 -23
  145. reflex/components/radix/themes/components/badge.py +2 -1
  146. reflex/components/radix/themes/components/badge.pyi +50 -57
  147. reflex/components/radix/themes/components/button.py +2 -1
  148. reflex/components/radix/themes/components/button.pyi +60 -79
  149. reflex/components/radix/themes/components/callout.py +2 -1
  150. reflex/components/radix/themes/components/callout.pyi +201 -258
  151. reflex/components/radix/themes/components/card.py +2 -1
  152. reflex/components/radix/themes/components/card.pyi +48 -56
  153. reflex/components/radix/themes/components/checkbox.py +2 -1
  154. reflex/components/radix/themes/components/checkbox.pyi +68 -62
  155. reflex/components/radix/themes/components/checkbox_cards.py +8 -3
  156. reflex/components/radix/themes/components/checkbox_cards.pyi +87 -44
  157. reflex/components/radix/themes/components/checkbox_group.py +2 -1
  158. reflex/components/radix/themes/components/checkbox_group.pyi +49 -40
  159. reflex/components/radix/themes/components/context_menu.py +2 -1
  160. reflex/components/radix/themes/components/context_menu.pyi +153 -147
  161. reflex/components/radix/themes/components/data_list.py +8 -7
  162. reflex/components/radix/themes/components/data_list.pyi +116 -78
  163. reflex/components/radix/themes/components/dialog.py +2 -1
  164. reflex/components/radix/themes/components/dialog.pyi +154 -161
  165. reflex/components/radix/themes/components/dropdown_menu.py +2 -1
  166. reflex/components/radix/themes/components/dropdown_menu.pyi +169 -163
  167. reflex/components/radix/themes/components/hover_card.py +2 -1
  168. reflex/components/radix/themes/components/hover_card.pyi +97 -107
  169. reflex/components/radix/themes/components/icon_button.py +2 -1
  170. reflex/components/radix/themes/components/icon_button.pyi +59 -82
  171. reflex/components/radix/themes/components/inset.py +10 -9
  172. reflex/components/radix/themes/components/inset.pyi +109 -61
  173. reflex/components/radix/themes/components/popover.py +2 -1
  174. reflex/components/radix/themes/components/popover.pyi +105 -112
  175. reflex/components/radix/themes/components/progress.py +2 -1
  176. reflex/components/radix/themes/components/progress.pyi +32 -24
  177. reflex/components/radix/themes/components/radio.py +2 -1
  178. reflex/components/radix/themes/components/radio.pyi +32 -23
  179. reflex/components/radix/themes/components/radio_cards.py +51 -3
  180. reflex/components/radix/themes/components/radio_cards.pyi +120 -44
  181. reflex/components/radix/themes/components/radio_group.py +5 -2
  182. reflex/components/radix/themes/components/radio_group.pyi +82 -77
  183. reflex/components/radix/themes/components/scroll_area.pyi +21 -21
  184. reflex/components/radix/themes/components/segmented_control.py +2 -1
  185. reflex/components/radix/themes/components/segmented_control.pyi +52 -46
  186. reflex/components/radix/themes/components/select.py +2 -1
  187. reflex/components/radix/themes/components/select.pyi +188 -164
  188. reflex/components/radix/themes/components/separator.py +5 -2
  189. reflex/components/radix/themes/components/separator.pyi +40 -24
  190. reflex/components/radix/themes/components/skeleton.py +7 -6
  191. reflex/components/radix/themes/components/skeleton.pyi +40 -26
  192. reflex/components/radix/themes/components/slider.py +2 -1
  193. reflex/components/radix/themes/components/slider.pyi +40 -31
  194. reflex/components/radix/themes/components/spinner.py +2 -1
  195. reflex/components/radix/themes/components/spinner.pyi +31 -22
  196. reflex/components/radix/themes/components/switch.py +2 -1
  197. reflex/components/radix/themes/components/switch.pyi +33 -25
  198. reflex/components/radix/themes/components/table.py +2 -1
  199. reflex/components/radix/themes/components/table.pyi +265 -404
  200. reflex/components/radix/themes/components/tabs.py +14 -1
  201. reflex/components/radix/themes/components/tabs.pyi +113 -92
  202. reflex/components/radix/themes/components/text_area.py +3 -2
  203. reflex/components/radix/themes/components/text_area.pyi +64 -66
  204. reflex/components/radix/themes/components/text_field.py +2 -1
  205. reflex/components/radix/themes/components/text_field.pyi +116 -140
  206. reflex/components/radix/themes/components/tooltip.pyi +32 -37
  207. reflex/components/radix/themes/layout/__init__.pyi +4 -7
  208. reflex/components/radix/themes/layout/base.py +10 -9
  209. reflex/components/radix/themes/layout/base.pyi +121 -31
  210. reflex/components/radix/themes/layout/box.pyi +39 -53
  211. reflex/components/radix/themes/layout/center.pyi +89 -58
  212. reflex/components/radix/themes/layout/container.py +4 -1
  213. reflex/components/radix/themes/layout/container.pyi +51 -58
  214. reflex/components/radix/themes/layout/flex.py +6 -5
  215. reflex/components/radix/themes/layout/flex.pyi +91 -61
  216. reflex/components/radix/themes/layout/grid.py +9 -8
  217. reflex/components/radix/themes/layout/grid.pyi +116 -64
  218. reflex/components/radix/themes/layout/list.pyi +173 -233
  219. reflex/components/radix/themes/layout/section.py +4 -1
  220. reflex/components/radix/themes/layout/section.pyi +50 -57
  221. reflex/components/radix/themes/layout/spacer.pyi +89 -58
  222. reflex/components/radix/themes/layout/stack.pyi +160 -160
  223. reflex/components/radix/themes/typography/__init__.pyi +0 -2
  224. reflex/components/radix/themes/typography/blockquote.py +3 -2
  225. reflex/components/radix/themes/typography/blockquote.pyi +58 -59
  226. reflex/components/radix/themes/typography/code.py +3 -2
  227. reflex/components/radix/themes/typography/code.pyi +57 -58
  228. reflex/components/radix/themes/typography/heading.py +5 -4
  229. reflex/components/radix/themes/typography/heading.pyi +71 -60
  230. reflex/components/radix/themes/typography/link.py +4 -3
  231. reflex/components/radix/themes/typography/link.pyi +74 -82
  232. reflex/components/radix/themes/typography/text.py +5 -4
  233. reflex/components/radix/themes/typography/text.pyi +330 -364
  234. reflex/components/react_player/audio.pyi +37 -36
  235. reflex/components/react_player/react_player.pyi +37 -38
  236. reflex/components/react_player/video.pyi +37 -36
  237. reflex/components/recharts/__init__.pyi +41 -42
  238. reflex/components/recharts/cartesian.pyi +384 -400
  239. reflex/components/recharts/charts.pyi +224 -231
  240. reflex/components/recharts/general.pyi +89 -96
  241. reflex/components/recharts/polar.pyi +97 -104
  242. reflex/components/recharts/recharts.pyi +37 -37
  243. reflex/components/sonner/toast.pyi +22 -27
  244. reflex/components/suneditor/editor.pyi +53 -58
  245. reflex/config.py +6 -0
  246. reflex/constants/event.py +1 -0
  247. reflex/experimental/layout.pyi +140 -194
  248. reflex/model.py +14 -2
  249. reflex/state.py +55 -45
  250. reflex/style.py +24 -13
  251. reflex/utils/codespaces.py +94 -0
  252. reflex/utils/compat.py +21 -0
  253. reflex/utils/exceptions.py +4 -0
  254. reflex/utils/format.py +25 -4
  255. reflex/utils/prerequisites.py +0 -13
  256. reflex/utils/pyi_generator.py +88 -61
  257. reflex/utils/types.py +83 -5
  258. reflex/vars.py +62 -5
  259. reflex/vars.pyi +23 -11
  260. {reflex-0.5.5a2.dist-info → reflex-0.5.6.dist-info}/METADATA +4 -5
  261. {reflex-0.5.5a2.dist-info → reflex-0.5.6.dist-info}/RECORD +264 -255
  262. {reflex-0.5.5a2.dist-info → reflex-0.5.6.dist-info}/LICENSE +0 -0
  263. {reflex-0.5.5a2.dist-info → reflex-0.5.6.dist-info}/WHEEL +0 -0
  264. {reflex-0.5.5a2.dist-info → reflex-0.5.6.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,
@@ -197,16 +198,6 @@ def _no_chain_background_task(
197
198
  raise TypeError(f"{fn} is marked as a background task, but is not async.")
198
199
 
199
200
 
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
201
  def _substate_key(
211
202
  token: str,
212
203
  state_cls_or_name: BaseState | Type[BaseState] | str | list[str],
@@ -313,10 +304,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
313
304
  # Vars inherited by the parent state.
314
305
  inherited_vars: ClassVar[Dict[str, Var]] = {}
315
306
 
316
- # Backend vars that are never sent to the client.
307
+ # Backend base vars that are never sent to the client.
317
308
  backend_vars: ClassVar[Dict[str, Any]] = {}
318
309
 
319
- # Backend vars inherited
310
+ # Backend base vars inherited
320
311
  inherited_backend_vars: ClassVar[Dict[str, Any]] = {}
321
312
 
322
313
  # The event handlers.
@@ -352,7 +343,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
352
343
  # The routing path that triggered the state
353
344
  router_data: Dict[str, Any] = {}
354
345
 
355
- # Per-instance copy of backend variable values
346
+ # Per-instance copy of backend base variable values
356
347
  _backend_vars: Dict[str, Any] = {}
357
348
 
358
349
  # The router data for the current page
@@ -409,11 +400,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
409
400
 
410
401
  # Create a fresh copy of the backend variables for this instance
411
402
  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
- }
403
+ {name: item for name, item in self.backend_vars.items()}
417
404
  )
418
405
 
419
406
  def __repr__(self) -> str:
@@ -500,25 +487,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
500
487
  new_backend_vars = {
501
488
  name: value
502
489
  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
490
+ if types.is_backend_base_variable(name, cls)
516
491
  }
517
492
 
518
493
  cls.backend_vars = {
519
494
  **cls.inherited_backend_vars,
520
495
  **new_backend_vars,
521
- **backend_computed_vars,
522
496
  }
523
497
 
524
498
  # Set the base and computed vars.
@@ -560,6 +534,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
560
534
  cls.computed_vars[newcv._var_name] = newcv
561
535
  cls.vars[newcv._var_name] = newcv
562
536
  continue
537
+ if types.is_backend_base_variable(name, mixin):
538
+ cls.backend_vars[name] = copy.deepcopy(value)
539
+ continue
563
540
  if events.get(name) is not None:
564
541
  continue
565
542
  if not cls._item_is_event_handler(name, value):
@@ -743,7 +720,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
743
720
  "dirty_substates",
744
721
  "router_data",
745
722
  }
746
- | RESERVED_BACKEND_VAR_NAMES
723
+ | types.RESERVED_BACKEND_VAR_NAMES
747
724
  )
748
725
 
749
726
  @classmethod
@@ -1096,10 +1073,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1096
1073
  setattr(self.parent_state, name, value)
1097
1074
  return
1098
1075
 
1099
- if (
1100
- types.is_backend_variable(name, self.__class__)
1101
- and name not in RESERVED_BACKEND_VAR_NAMES
1102
- ):
1076
+ if name in self.backend_vars:
1103
1077
  self._backend_vars.__setitem__(name, value)
1104
1078
  self.dirty_vars.add(name)
1105
1079
  self._mark_dirty()
@@ -1550,11 +1524,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1550
1524
  if self.computed_vars[cvar].needs_update(instance=self)
1551
1525
  )
1552
1526
 
1553
- def _dirty_computed_vars(self, from_vars: set[str] | None = None) -> set[str]:
1527
+ def _dirty_computed_vars(
1528
+ self, from_vars: set[str] | None = None, include_backend: bool = True
1529
+ ) -> set[str]:
1554
1530
  """Determine ComputedVars that need to be recalculated based on the given vars.
1555
1531
 
1556
1532
  Args:
1557
1533
  from_vars: find ComputedVar that depend on this set of vars. If unspecified, will use the dirty_vars.
1534
+ include_backend: whether to include backend vars in the calculation.
1558
1535
 
1559
1536
  Returns:
1560
1537
  Set of computed vars to include in the delta.
@@ -1563,6 +1540,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1563
1540
  cvar
1564
1541
  for dirty_var in from_vars or self.dirty_vars
1565
1542
  for cvar in self._computed_var_dependencies[dirty_var]
1543
+ if include_backend or not self.computed_vars[cvar]._backend
1566
1544
  )
1567
1545
 
1568
1546
  @classmethod
@@ -1598,19 +1576,23 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1598
1576
  self.dirty_vars.update(self._always_dirty_computed_vars)
1599
1577
  self._mark_dirty()
1600
1578
 
1579
+ frontend_computed_vars: set[str] = {
1580
+ name for name, cv in self.computed_vars.items() if not cv._backend
1581
+ }
1582
+
1601
1583
  # Return the dirty vars for this instance, any cached/dependent computed vars,
1602
1584
  # and always dirty computed vars (cache=False)
1603
1585
  delta_vars = (
1604
1586
  self.dirty_vars.intersection(self.base_vars)
1605
- .union(self.dirty_vars.intersection(self.computed_vars))
1606
- .union(self._dirty_computed_vars())
1587
+ .union(self.dirty_vars.intersection(frontend_computed_vars))
1588
+ .union(self._dirty_computed_vars(include_backend=False))
1607
1589
  .union(self._always_dirty_computed_vars)
1608
1590
  )
1609
1591
 
1610
1592
  subdelta = {
1611
1593
  prop: getattr(self, prop)
1612
1594
  for prop in delta_vars
1613
- if not types.is_backend_variable(prop, self.__class__)
1595
+ if not types.is_backend_base_variable(prop, type(self))
1614
1596
  }
1615
1597
  if len(subdelta) > 0:
1616
1598
  delta[self.get_full_name()] = subdelta
@@ -1739,12 +1721,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1739
1721
  else self.get_value(getattr(self, prop_name))
1740
1722
  )
1741
1723
  for prop_name, cv in self.computed_vars.items()
1724
+ if not cv._backend
1742
1725
  }
1743
1726
  elif include_computed:
1744
1727
  computed_vars = {
1745
1728
  # Include the computed vars.
1746
1729
  prop_name: self.get_value(getattr(self, prop_name))
1747
- for prop_name in self.computed_vars
1730
+ for prop_name, cv in self.computed_vars.items()
1731
+ if not cv._backend
1748
1732
  }
1749
1733
  else:
1750
1734
  computed_vars = {}
@@ -1757,6 +1741,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1757
1741
  for v in self.substates.values()
1758
1742
  ]:
1759
1743
  d.update(substate_d)
1744
+
1760
1745
  return d
1761
1746
 
1762
1747
  async def __aenter__(self) -> BaseState:
@@ -2202,7 +2187,14 @@ class StateManager(Base, ABC):
2202
2187
  """
2203
2188
  redis = prerequisites.get_redis()
2204
2189
  if redis is not None:
2205
- return StateManagerRedis(state=state, redis=redis)
2190
+ # make sure expiration values are obtained only from the config object on creation
2191
+ config = get_config()
2192
+ return StateManagerRedis(
2193
+ state=state,
2194
+ redis=redis,
2195
+ token_expiration=config.redis_token_expiration,
2196
+ lock_expiration=config.redis_lock_expiration,
2197
+ )
2206
2198
  return StateManagerMemory(state=state)
2207
2199
 
2208
2200
  @abstractmethod
@@ -2326,6 +2318,24 @@ def _dill_reduce_state(pickler, obj):
2326
2318
  dill.Pickler.dispatch[type](pickler, obj)
2327
2319
 
2328
2320
 
2321
+ def _default_lock_expiration() -> int:
2322
+ """Get the default lock expiration time.
2323
+
2324
+ Returns:
2325
+ The default lock expiration time.
2326
+ """
2327
+ return get_config().redis_lock_expiration
2328
+
2329
+
2330
+ def _default_token_expiration() -> int:
2331
+ """Get the default token expiration time.
2332
+
2333
+ Returns:
2334
+ The default token expiration time.
2335
+ """
2336
+ return get_config().redis_token_expiration
2337
+
2338
+
2329
2339
  class StateManagerRedis(StateManager):
2330
2340
  """A state manager that stores states in redis."""
2331
2341
 
@@ -2333,10 +2343,10 @@ class StateManagerRedis(StateManager):
2333
2343
  redis: Redis
2334
2344
 
2335
2345
  # The token expiration time (s).
2336
- token_expiration: int = constants.Expiration.TOKEN
2346
+ token_expiration: int = pydantic.Field(default_factory=_default_token_expiration)
2337
2347
 
2338
2348
  # The maximum time to hold a lock (ms).
2339
- lock_expiration: int = constants.Expiration.LOCK
2349
+ lock_expiration: int = pydantic.Field(default_factory=_default_lock_expiration)
2340
2350
 
2341
2351
  # The keyspace subscription string when redis is waiting for lock to be released
2342
2352
  _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(