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
@@ -9,20 +9,15 @@ import inspect
9
9
  import logging
10
10
  import re
11
11
  import subprocess
12
- import textwrap
13
12
  import typing
13
+ from fileinput import FileInput
14
14
  from inspect import getfullargspec
15
+ from itertools import chain
15
16
  from multiprocessing import Pool, cpu_count
16
17
  from pathlib import Path
17
18
  from types import ModuleType, SimpleNamespace
18
19
  from typing import Any, Callable, Iterable, Type, get_args
19
20
 
20
- try:
21
- import black
22
- import black.mode
23
- except ImportError:
24
- black = None
25
-
26
21
  from reflex.components.component import Component
27
22
  from reflex.utils import types as rx_types
28
23
  from reflex.vars import Var
@@ -63,6 +58,7 @@ EXCLUDED_PROPS = [
63
58
  DEFAULT_TYPING_IMPORTS = {
64
59
  "overload",
65
60
  "Any",
61
+ "Callable",
66
62
  "Dict",
67
63
  # "List",
68
64
  "Literal",
@@ -70,6 +66,15 @@ DEFAULT_TYPING_IMPORTS = {
70
66
  "Union",
71
67
  }
72
68
 
69
+ # TODO: fix import ordering and unused imports with ruff later
70
+ DEFAULT_IMPORTS = {
71
+ "typing": sorted(DEFAULT_TYPING_IMPORTS),
72
+ "reflex.vars": ["Var", "BaseVar", "ComputedVar"],
73
+ "reflex.components.core.breakpoints": ["Breakpoints"],
74
+ "reflex.event": ["EventChain", "EventHandler", "EventSpec"],
75
+ "reflex.style": ["Style"],
76
+ }
77
+
73
78
 
74
79
  def _walk_files(path):
75
80
  """Walk all files in a path.
@@ -112,6 +117,9 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
112
117
 
113
118
  Returns:
114
119
  The resolved type hint as a str.
120
+
121
+ Raises:
122
+ TypeError: If the value name is not visible in the type hint globals.
115
123
  """
116
124
  res = ""
117
125
  args = get_args(value)
@@ -126,6 +134,7 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
126
134
  for arg in value.__args__
127
135
  if arg is not type(None)
128
136
  ]
137
+ res_args.sort()
129
138
  if len(res_args) == 1:
130
139
  return f"Optional[{res_args[0]}]"
131
140
  else:
@@ -136,6 +145,7 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
136
145
  _get_type_hint(arg, type_hint_globals, rx_types.is_optional(arg))
137
146
  for arg in value.__args__
138
147
  ]
148
+ res_args.sort()
139
149
  return f"Union[{', '.join(res_args)}]"
140
150
 
141
151
  if args:
@@ -148,9 +158,22 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
148
158
  if arg is not type(None)
149
159
  ]
150
160
  )
161
+
162
+ if value.__name__ not in type_hint_globals:
163
+ raise TypeError(
164
+ f"{value.__module__ + '.' + value.__name__} is not a default import, "
165
+ "add it to DEFAULT_IMPORTS in pyi_generator.py"
166
+ )
167
+
151
168
  res = f"{value.__name__}[{', '.join(inner_container_type_args)}]"
152
169
 
153
170
  if value.__name__ == "Var":
171
+ args = list(
172
+ chain.from_iterable(
173
+ [get_args(arg) if rx_types.is_union(arg) else [arg] for arg in args]
174
+ )
175
+ )
176
+
154
177
  # For Var types, Union with the inner args so they can be passed directly.
155
178
  types = [res] + [
156
179
  _get_type_hint(arg, type_hint_globals, is_optional=False)
@@ -198,22 +221,10 @@ def _generate_imports(typing_imports: Iterable[str]) -> list[ast.ImportFrom]:
198
221
  The list of import statements.
199
222
  """
200
223
  return [
201
- ast.ImportFrom(
202
- module="typing",
203
- names=[ast.alias(name=imp) for imp in sorted(typing_imports)],
204
- ),
205
- *ast.parse( # type: ignore
206
- textwrap.dedent(
207
- """
208
- from reflex.vars import Var, BaseVar, ComputedVar
209
- from reflex.event import EventChain, EventHandler, EventSpec
210
- from reflex.style import Style"""
211
- )
212
- ).body,
213
- # *[
214
- # ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values])
215
- # for name, values in EXTRA_IMPORTS.items()
216
- # ],
224
+ *[
225
+ ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values])
226
+ for name, values in DEFAULT_IMPORTS.items()
227
+ ],
217
228
  ]
218
229
 
219
230
 
@@ -419,7 +430,7 @@ def _generate_component_create_functiondef(
419
430
  ast.arg(
420
431
  arg=trigger,
421
432
  annotation=ast.Name(
422
- id="Optional[Union[EventHandler, EventSpec, list, function, BaseVar]]"
433
+ id="Optional[Union[EventHandler, EventSpec, list, Callable, BaseVar]]"
423
434
  ),
424
435
  ),
425
436
  ast.Constant(value=None),
@@ -488,9 +499,11 @@ def _generate_staticmethod_call_functiondef(
488
499
  kwonlyargs=[],
489
500
  kw_defaults=[],
490
501
  kwarg=ast.arg(arg="props"),
491
- defaults=[ast.Constant(value=default) for default in fullspec.defaults]
492
- if fullspec.defaults
493
- else [],
502
+ defaults=(
503
+ [ast.Constant(value=default) for default in fullspec.defaults]
504
+ if fullspec.defaults
505
+ else []
506
+ ),
494
507
  )
495
508
  definition = ast.FunctionDef(
496
509
  name="__call__",
@@ -578,7 +591,7 @@ class StubGenerator(ast.NodeTransformer):
578
591
  # Track the last class node that was visited.
579
592
  self.current_class = None
580
593
  # These imports will be included in the AST of stub files.
581
- self.typing_imports = DEFAULT_TYPING_IMPORTS
594
+ self.typing_imports = DEFAULT_TYPING_IMPORTS.copy()
582
595
  # Whether those typing imports have been inserted yet.
583
596
  self.inserted_imports = False
584
597
  # Collected import statements from the module.
@@ -646,7 +659,9 @@ class StubGenerator(ast.NodeTransformer):
646
659
  self.import_statements.append(ast.unparse(node))
647
660
  if not self.inserted_imports:
648
661
  self.inserted_imports = True
649
- return _generate_imports(self.typing_imports) + [node]
662
+ default_imports = _generate_imports(self.typing_imports)
663
+ self.import_statements.extend(ast.unparse(i) for i in default_imports)
664
+ return default_imports + [node]
650
665
  return node
651
666
 
652
667
  def visit_ImportFrom(
@@ -839,37 +854,25 @@ class PyiGenerator:
839
854
  modules: list = []
840
855
  root: str = ""
841
856
  current_module: Any = {}
857
+ written_files: list[str] = []
842
858
 
843
859
  def _write_pyi_file(self, module_path: Path, source: str):
844
860
  relpath = str(_relative_to_pwd(module_path)).replace("\\", "/")
845
- pyi_content = [
846
- f'"""Stub file for {relpath}"""',
847
- "# ------------------- DO NOT EDIT ----------------------",
848
- "# This file was generated by `reflex/utils/pyi_generator.py`!",
849
- "# ------------------------------------------------------",
850
- "",
851
- ]
852
- if black is not None:
853
- for formatted_line in black.format_file_contents(
854
- src_contents=source,
855
- fast=True,
856
- mode=black.mode.Mode(is_pyi=True),
857
- ).splitlines():
858
- # Bit of a hack here, since the AST cannot represent comments.
859
- if (
860
- "def create(" in formatted_line
861
- or "Figure" in formatted_line
862
- or "Var[Template]" in formatted_line
863
- ):
864
- pyi_content.append(formatted_line + " # type: ignore")
865
- else:
866
- pyi_content.append(formatted_line)
867
- pyi_content.append("") # add empty line at the end for formatting
868
- else:
869
- pyi_content = source.splitlines()
861
+ pyi_content = (
862
+ "\n".join(
863
+ [
864
+ f'"""Stub file for {relpath}"""',
865
+ "# ------------------- DO NOT EDIT ----------------------",
866
+ "# This file was generated by `reflex/utils/pyi_generator.py`!",
867
+ "# ------------------------------------------------------",
868
+ "",
869
+ ]
870
+ )
871
+ + source
872
+ )
870
873
 
871
874
  pyi_path = module_path.with_suffix(".pyi")
872
- pyi_path.write_text("\n".join(pyi_content))
875
+ pyi_path.write_text(pyi_content)
873
876
  logger.info(f"Wrote {relpath}")
874
877
 
875
878
  def _get_init_lazy_imports(self, mod, new_tree):
@@ -903,7 +906,7 @@ class PyiGenerator:
903
906
  text += ast.unparse(new_tree) + "\n"
904
907
  return text
905
908
 
906
- def _scan_file(self, module_path: Path):
909
+ def _scan_file(self, module_path: Path) -> str | None:
907
910
  module_import = (
908
911
  _relative_to_pwd(module_path)
909
912
  .with_suffix("")
@@ -930,21 +933,25 @@ class PyiGenerator:
930
933
  ast.parse(inspect.getsource(module))
931
934
  )
932
935
  init_imports = self._get_init_lazy_imports(module, new_tree)
933
- if init_imports:
934
- self._write_pyi_file(module_path, init_imports)
936
+ if not init_imports:
937
+ return
938
+ self._write_pyi_file(module_path, init_imports)
935
939
  else:
936
940
  new_tree = StubGenerator(module, class_names).visit(
937
941
  ast.parse(inspect.getsource(module))
938
942
  )
939
943
  self._write_pyi_file(module_path, ast.unparse(new_tree))
944
+ return str(module_path.with_suffix(".pyi").resolve())
940
945
 
941
946
  def _scan_files_multiprocess(self, files: list[Path]):
942
947
  with Pool(processes=cpu_count()) as pool:
943
- pool.map(self._scan_file, files)
948
+ self.written_files.extend(f for f in pool.map(self._scan_file, files) if f)
944
949
 
945
950
  def _scan_files(self, files: list[Path]):
946
951
  for file in files:
947
- self._scan_file(file)
952
+ pyi_path = self._scan_file(file)
953
+ if pyi_path:
954
+ self.written_files.append(pyi_path)
948
955
 
949
956
  def scan_all(self, targets, changed_files: list[Path] | None = None):
950
957
  """Scan all targets for class inheriting Component and generate the .pyi files.
@@ -960,7 +967,6 @@ class PyiGenerator:
960
967
  target_path.is_file()
961
968
  and target_path.suffix == ".py"
962
969
  and target_path.name not in EXCLUDED_FILES
963
- and "reflex/components" in str(target_path)
964
970
  ):
965
971
  file_targets.append(target_path)
966
972
  continue
@@ -993,3 +999,24 @@ class PyiGenerator:
993
999
  self._scan_files(file_targets)
994
1000
  else:
995
1001
  self._scan_files_multiprocess(file_targets)
1002
+
1003
+ # Fix generated pyi files with ruff.
1004
+ subprocess.run(["ruff", "format", *self.written_files])
1005
+ subprocess.run(["ruff", "check", "--fix", *self.written_files])
1006
+
1007
+ # For some reason, we need to format the __init__.pyi files again after fixing...
1008
+ init_files = [f for f in self.written_files if "/__init__.pyi" in f]
1009
+ subprocess.run(["ruff", "format", *init_files])
1010
+
1011
+ # Post-process the generated pyi files to add hacky type: ignore comments
1012
+ for file_path in self.written_files:
1013
+ with FileInput(file_path, inplace=True) as f:
1014
+ for line in f:
1015
+ # Hack due to ast not supporting comments in the tree.
1016
+ if (
1017
+ "def create(" in line
1018
+ or "Var[Figure]" in line
1019
+ or "Var[Template]" in line
1020
+ ):
1021
+ line = line.rstrip() + " # type: ignore\n"
1022
+ print(line, end="")
reflex/utils/types.py CHANGED
@@ -6,10 +6,11 @@ import contextlib
6
6
  import inspect
7
7
  import sys
8
8
  import types
9
- from functools import wraps
9
+ from functools import cached_property, wraps
10
10
  from typing import (
11
11
  Any,
12
12
  Callable,
13
+ ClassVar,
13
14
  Dict,
14
15
  Iterable,
15
16
  List,
@@ -26,6 +27,9 @@ from typing import (
26
27
 
27
28
  import sqlalchemy
28
29
 
30
+ import reflex
31
+ from reflex.components.core.breakpoints import Breakpoints
32
+
29
33
  try:
30
34
  from pydantic.v1.fields import ModelField
31
35
  except ModuleNotFoundError:
@@ -99,6 +103,12 @@ PrimitiveToAnnotation = {
99
103
  dict: Dict,
100
104
  }
101
105
 
106
+ RESERVED_BACKEND_VAR_NAMES = {
107
+ "_abc_impl",
108
+ "_backend_vars",
109
+ "_was_touched",
110
+ }
111
+
102
112
 
103
113
  class Unset:
104
114
  """A class to represent an unset value.
@@ -324,12 +334,44 @@ def get_base_class(cls: GenericType) -> Type:
324
334
  return get_base_class(cls.__origin__) if is_generic_alias(cls) else cls
325
335
 
326
336
 
327
- def _issubclass(cls: GenericType, cls_check: GenericType) -> bool:
337
+ def _breakpoints_satisfies_typing(cls_check: GenericType, instance: Any) -> bool:
338
+ """Check if the breakpoints instance satisfies the typing.
339
+
340
+ Args:
341
+ cls_check: The class to check against.
342
+ instance: The instance to check.
343
+
344
+ Returns:
345
+ Whether the breakpoints instance satisfies the typing.
346
+ """
347
+ cls_check_base = get_base_class(cls_check)
348
+
349
+ if cls_check_base == Breakpoints:
350
+ _, expected_type = get_args(cls_check)
351
+ if is_literal(expected_type):
352
+ for value in instance.values():
353
+ if not isinstance(value, str) or value not in get_args(expected_type):
354
+ return False
355
+ return True
356
+ elif isinstance(cls_check_base, tuple):
357
+ # union type, so check all types
358
+ return any(
359
+ _breakpoints_satisfies_typing(type_to_check, instance)
360
+ for type_to_check in get_args(cls_check)
361
+ )
362
+ elif cls_check_base == reflex.vars.Var and "__args__" in cls_check.__dict__:
363
+ return _breakpoints_satisfies_typing(get_args(cls_check)[0], instance)
364
+
365
+ return False
366
+
367
+
368
+ def _issubclass(cls: GenericType, cls_check: GenericType, instance: Any = None) -> bool:
328
369
  """Check if a class is a subclass of another class.
329
370
 
330
371
  Args:
331
372
  cls: The class to check.
332
373
  cls_check: The class to check against.
374
+ instance: An instance of cls to aid in checking generics.
333
375
 
334
376
  Returns:
335
377
  Whether the class is a subclass of the other class.
@@ -351,6 +393,10 @@ def _issubclass(cls: GenericType, cls_check: GenericType) -> bool:
351
393
  if isinstance(cls_base, tuple):
352
394
  return False
353
395
 
396
+ # Check that fields of breakpoints match the expected values.
397
+ if isinstance(instance, Breakpoints):
398
+ return _breakpoints_satisfies_typing(cls_check, instance)
399
+
354
400
  # Check if the types match.
355
401
  try:
356
402
  return cls_check_base == Any or issubclass(cls_base, cls_check_base)
@@ -403,7 +449,7 @@ def is_valid_var_type(type_: Type) -> bool:
403
449
  return _issubclass(type_, StateVar) or serializers.has_serializer(type_)
404
450
 
405
451
 
406
- def is_backend_variable(name: str, cls: Type | None = None) -> bool:
452
+ def is_backend_base_variable(name: str, cls: Type) -> bool:
407
453
  """Check if this variable name correspond to a backend variable.
408
454
 
409
455
  Args:
@@ -413,9 +459,41 @@ def is_backend_variable(name: str, cls: Type | None = None) -> bool:
413
459
  Returns:
414
460
  bool: The result of the check
415
461
  """
416
- if cls is not None and name.startswith(f"_{cls.__name__}__"):
462
+ if name in RESERVED_BACKEND_VAR_NAMES:
463
+ return False
464
+
465
+ if not name.startswith("_"):
466
+ return False
467
+
468
+ if name.startswith("__"):
469
+ return False
470
+
471
+ if name.startswith(f"_{cls.__name__}__"):
472
+ return False
473
+
474
+ hints = get_type_hints(cls)
475
+ if name in hints:
476
+ hint = get_origin(hints[name])
477
+ if hint == ClassVar:
478
+ return False
479
+
480
+ if name in cls.inherited_backend_vars:
417
481
  return False
418
- return name.startswith("_") and not name.startswith("__")
482
+
483
+ if name in cls.__dict__:
484
+ value = cls.__dict__[name]
485
+ if type(value) == classmethod:
486
+ return False
487
+ if callable(value):
488
+ return False
489
+ from reflex.vars import ComputedVar
490
+
491
+ if isinstance(
492
+ value, (types.FunctionType, property, cached_property, ComputedVar)
493
+ ):
494
+ return False
495
+
496
+ return True
419
497
 
420
498
 
421
499
  def check_type_in_allowed_types(value_type: Type, allowed_types: Iterable) -> bool:
reflex/vars.py CHANGED
@@ -36,7 +36,12 @@ from typing import (
36
36
  from reflex import constants
37
37
  from reflex.base import Base
38
38
  from reflex.utils import console, imports, serializers, types
39
- from reflex.utils.exceptions import VarAttributeError, VarTypeError, VarValueError
39
+ from reflex.utils.exceptions import (
40
+ VarAttributeError,
41
+ VarDependencyError,
42
+ VarTypeError,
43
+ VarValueError,
44
+ )
40
45
 
41
46
  # This module used to export ImportVar itself, so we still import it for export here
42
47
  from reflex.utils.imports import (
@@ -416,7 +421,7 @@ class Var:
416
421
 
417
422
  if _var_is_string is None and type_ is str:
418
423
  console.deprecate(
419
- feature_name="Creating a Var from a string without specifying _var_is_string",
424
+ feature_name=f"Creating a Var ({value}) from a string without specifying _var_is_string",
420
425
  reason=(
421
426
  "Specify _var_is_string=False to create a Var that is not a string literal. "
422
427
  "In the future, creating a Var from a string will be treated as a string literal "
@@ -1939,6 +1944,9 @@ class ComputedVar(Var, property):
1939
1944
  # Whether to track dependencies and cache computed values
1940
1945
  _cache: bool = dataclasses.field(default=False)
1941
1946
 
1947
+ # Whether the computed var is a backend var
1948
+ _backend: bool = dataclasses.field(default=False)
1949
+
1942
1950
  # The initial value of the computed var
1943
1951
  _initial_value: Any | types.Unset = dataclasses.field(default=types.Unset())
1944
1952
 
@@ -1959,6 +1967,7 @@ class ComputedVar(Var, property):
1959
1967
  deps: Optional[List[Union[str, Var]]] = None,
1960
1968
  auto_deps: bool = True,
1961
1969
  interval: Optional[Union[int, datetime.timedelta]] = None,
1970
+ backend: bool | None = None,
1962
1971
  **kwargs,
1963
1972
  ):
1964
1973
  """Initialize a ComputedVar.
@@ -1970,8 +1979,16 @@ class ComputedVar(Var, property):
1970
1979
  deps: Explicit var dependencies to track.
1971
1980
  auto_deps: Whether var dependencies should be auto-determined.
1972
1981
  interval: Interval at which the computed var should be updated.
1982
+ backend: Whether the computed var is a backend var.
1973
1983
  **kwargs: additional attributes to set on the instance
1984
+
1985
+ Raises:
1986
+ TypeError: If the computed var dependencies are not Var instances or var names.
1974
1987
  """
1988
+ if backend is None:
1989
+ backend = fget.__name__.startswith("_")
1990
+ self._backend = backend
1991
+
1975
1992
  self._initial_value = initial_value
1976
1993
  self._cache = cache
1977
1994
  if isinstance(interval, int):
@@ -1979,6 +1996,15 @@ class ComputedVar(Var, property):
1979
1996
  self._update_interval = interval
1980
1997
  if deps is None:
1981
1998
  deps = []
1999
+ else:
2000
+ for dep in deps:
2001
+ if isinstance(dep, Var):
2002
+ continue
2003
+ if isinstance(dep, str) and dep != "":
2004
+ continue
2005
+ raise TypeError(
2006
+ "ComputedVar dependencies must be Var instances or var names (non-empty strings)."
2007
+ )
1982
2008
  self._static_deps = {
1983
2009
  dep._var_name if isinstance(dep, Var) else dep for dep in deps
1984
2010
  }
@@ -2006,6 +2032,7 @@ class ComputedVar(Var, property):
2006
2032
  deps=kwargs.get("deps", self._static_deps),
2007
2033
  auto_deps=kwargs.get("auto_deps", self._auto_deps),
2008
2034
  interval=kwargs.get("interval", self._update_interval),
2035
+ backend=kwargs.get("backend", self._backend),
2009
2036
  _var_name=kwargs.get("_var_name", self._var_name),
2010
2037
  _var_type=kwargs.get("_var_type", self._var_type),
2011
2038
  _var_is_local=kwargs.get("_var_is_local", self._var_is_local),
@@ -2157,8 +2184,21 @@ class ComputedVar(Var, property):
2157
2184
  obj=ref_obj,
2158
2185
  )
2159
2186
  )
2160
- else:
2161
- # normal attribute access
2187
+ # recurse into property fget functions
2188
+ elif isinstance(ref_obj, property) and not isinstance(
2189
+ ref_obj, ComputedVar
2190
+ ):
2191
+ d.update(
2192
+ self._deps(
2193
+ objclass=objclass,
2194
+ obj=ref_obj.fget, # type: ignore
2195
+ )
2196
+ )
2197
+ elif (
2198
+ instruction.argval in objclass.backend_vars
2199
+ or instruction.argval in objclass.vars
2200
+ ):
2201
+ # var access
2162
2202
  d.add(instruction.argval)
2163
2203
  elif instruction.opname == "LOAD_CONST" and isinstance(
2164
2204
  instruction.argval, CodeType
@@ -2203,6 +2243,8 @@ def computed_var(
2203
2243
  deps: Optional[List[Union[str, Var]]] = None,
2204
2244
  auto_deps: bool = True,
2205
2245
  interval: Optional[Union[datetime.timedelta, int]] = None,
2246
+ backend: bool | None = None,
2247
+ _deprecated_cached_var: bool = False,
2206
2248
  **kwargs,
2207
2249
  ) -> ComputedVar | Callable[[Callable[[BaseState], Any]], ComputedVar]:
2208
2250
  """A ComputedVar decorator with or without kwargs.
@@ -2214,6 +2256,8 @@ def computed_var(
2214
2256
  deps: Explicit var dependencies to track.
2215
2257
  auto_deps: Whether var dependencies should be auto-determined.
2216
2258
  interval: Interval at which the computed var should be updated.
2259
+ backend: Whether the computed var is a backend var.
2260
+ _deprecated_cached_var: Indicate usage of deprecated cached_var partial function.
2217
2261
  **kwargs: additional attributes to set on the instance
2218
2262
 
2219
2263
  Returns:
@@ -2221,10 +2265,22 @@ def computed_var(
2221
2265
 
2222
2266
  Raises:
2223
2267
  ValueError: If caching is disabled and an update interval is set.
2268
+ VarDependencyError: If user supplies dependencies without caching.
2224
2269
  """
2270
+ if _deprecated_cached_var:
2271
+ console.deprecate(
2272
+ feature_name="cached_var",
2273
+ reason=("Use @rx.var(cache=True) instead of @rx.cached_var."),
2274
+ deprecation_version="0.5.6",
2275
+ removal_version="0.6.0",
2276
+ )
2277
+
2225
2278
  if cache is False and interval is not None:
2226
2279
  raise ValueError("Cannot set update interval without caching.")
2227
2280
 
2281
+ if cache is False and (deps is not None or auto_deps is False):
2282
+ raise VarDependencyError("Cannot track dependencies without caching.")
2283
+
2228
2284
  if fget is not None:
2229
2285
  return ComputedVar(fget=fget, cache=cache)
2230
2286
 
@@ -2236,6 +2292,7 @@ def computed_var(
2236
2292
  deps=deps,
2237
2293
  auto_deps=auto_deps,
2238
2294
  interval=interval,
2295
+ backend=backend,
2239
2296
  **kwargs,
2240
2297
  )
2241
2298
 
@@ -2243,7 +2300,7 @@ def computed_var(
2243
2300
 
2244
2301
 
2245
2302
  # Partial function of computed_var with cache=True
2246
- cached_var = functools.partial(computed_var, cache=True)
2303
+ cached_var = functools.partial(computed_var, cache=True, _deprecated_cached_var=True)
2247
2304
 
2248
2305
 
2249
2306
  class CallableVar(BaseVar):
reflex/vars.pyi CHANGED
@@ -1,16 +1,9 @@
1
- """ Generated with stubgen from mypy, then manually edited, do not regen."""
1
+ """Generated with stubgen from mypy, then manually edited, do not regen."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
5
  import datetime
6
6
  from dataclasses import dataclass
7
- from _typeshed import Incomplete
8
- from reflex import constants as constants
9
- from reflex.base import Base as Base
10
- from reflex.state import State as State
11
- from reflex.state import BaseState as BaseState
12
- from reflex.utils import console as console, format as format, types as types
13
- from reflex.utils.imports import ImportVar, ImportDict, ParsedImportDict
14
7
  from types import FunctionType
15
8
  from typing import (
16
9
  Any,
@@ -23,10 +16,21 @@ from typing import (
23
16
  Tuple,
24
17
  Type,
25
18
  Union,
26
- overload,
27
19
  _GenericAlias, # type: ignore
20
+ overload,
28
21
  )
29
22
 
23
+ from _typeshed import Incomplete
24
+
25
+ from reflex import constants as constants
26
+ from reflex.base import Base as Base
27
+ from reflex.state import BaseState as BaseState
28
+ from reflex.state import State as State
29
+ from reflex.utils import console as console
30
+ from reflex.utils import format as format
31
+ from reflex.utils import types as types
32
+ from reflex.utils.imports import ImportDict, ParsedImportDict
33
+
30
34
  USED_VARIABLES: Incomplete
31
35
 
32
36
  def get_unique_variable_name() -> str: ...
@@ -51,11 +55,19 @@ class Var:
51
55
  _var_data: VarData | None = None
52
56
  @classmethod
53
57
  def create(
54
- cls, value: Any, _var_is_local: bool = True, _var_is_string: bool | None = None, _var_data: VarData | None = None,
58
+ cls,
59
+ value: Any,
60
+ _var_is_local: bool = True,
61
+ _var_is_string: bool | None = None,
62
+ _var_data: VarData | None = None,
55
63
  ) -> Optional[Var]: ...
56
64
  @classmethod
57
65
  def create_safe(
58
- cls, value: Any, _var_is_local: bool = True, _var_is_string: bool | None = None, _var_data: VarData | None = None,
66
+ cls,
67
+ value: Any,
68
+ _var_is_local: bool = True,
69
+ _var_is_string: bool | None = None,
70
+ _var_data: VarData | None = None,
59
71
  ) -> Var: ...
60
72
  @classmethod
61
73
  def __class_getitem__(cls, type_: Type) -> _GenericAlias: ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: reflex
3
- Version: 0.5.5a2
3
+ Version: 0.5.6a1
4
4
  Summary: Web apps in pure Python.
5
5
  Home-page: https://reflex.dev
6
6
  License: Apache-2.0
@@ -28,7 +28,7 @@ Requires-Dist: jinja2 (>=3.1.2,<4.0)
28
28
  Requires-Dist: lazy_loader (>=0.4)
29
29
  Requires-Dist: packaging (>=23.1,<25.0)
30
30
  Requires-Dist: platformdirs (>=3.10.0,<5.0)
31
- Requires-Dist: psutil (>=5.9.4,<6.0)
31
+ Requires-Dist: psutil (>=5.9.4,<7.0)
32
32
  Requires-Dist: pydantic (>=1.10.2,<3.0)
33
33
  Requires-Dist: python-engineio (!=4.6.0)
34
34
  Requires-Dist: python-multipart (>=0.0.5,<0.1)
@@ -36,14 +36,13 @@ Requires-Dist: python-socketio (>=5.7.0,<6.0)
36
36
  Requires-Dist: redis (>=4.3.5,<6.0)
37
37
  Requires-Dist: reflex-hosting-cli (>=0.1.2,<2.0)
38
38
  Requires-Dist: rich (>=13.0.0,<14.0)
39
- Requires-Dist: setuptools (>=69.1.1,<70.0)
39
+ Requires-Dist: setuptools (>=69.1.1,<70.2)
40
40
  Requires-Dist: sqlmodel (>=0.0.14,<0.1)
41
41
  Requires-Dist: starlette-admin (>=0.11.0,<1.0)
42
42
  Requires-Dist: tomlkit (>=0.12.4,<1.0)
43
43
  Requires-Dist: twine (>=4.0.0,<6.0)
44
44
  Requires-Dist: typer (>=0.4.2,<1.0)
45
- Requires-Dist: uvicorn (>=0.20.0,<0.21.0) ; python_version < "3.12"
46
- Requires-Dist: uvicorn (>=0.24.0,<0.25.0) ; python_version >= "3.12"
45
+ Requires-Dist: uvicorn (>=0.20.0)
47
46
  Requires-Dist: watchdog (>=2.3.1,<5.0)
48
47
  Requires-Dist: watchfiles (>=0.19.0,<1.0)
49
48
  Requires-Dist: wheel (>=0.42.0,<1.0)