reflex 0.7.14a5__py3-none-any.whl → 0.8.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (205) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +16 -10
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -6
  7. reflex/.templates/web/app/entry.client.js +8 -0
  8. reflex/.templates/web/app/routes.js +10 -0
  9. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -32
  10. reflex/.templates/web/postcss.config.js +1 -1
  11. reflex/.templates/web/react-router.config.js +6 -0
  12. reflex/.templates/web/utils/client_side_routing.js +21 -19
  13. reflex/.templates/web/utils/react-theme.js +89 -0
  14. reflex/.templates/web/utils/state.js +155 -67
  15. reflex/.templates/web/vite.config.js +32 -0
  16. reflex/__init__.py +1 -6
  17. reflex/__init__.pyi +0 -4
  18. reflex/app.py +69 -132
  19. reflex/base.py +1 -87
  20. reflex/compiler/compiler.py +40 -3
  21. reflex/compiler/utils.py +54 -28
  22. reflex/components/__init__.py +0 -2
  23. reflex/components/__init__.pyi +0 -3
  24. reflex/components/base/__init__.py +1 -5
  25. reflex/components/base/__init__.pyi +4 -6
  26. reflex/components/base/app_wrap.pyi +5 -4
  27. reflex/components/base/body.pyi +5 -4
  28. reflex/components/base/document.py +18 -14
  29. reflex/components/base/document.pyi +83 -27
  30. reflex/components/base/error_boundary.pyi +5 -4
  31. reflex/components/base/fragment.pyi +5 -4
  32. reflex/components/base/link.pyi +9 -7
  33. reflex/components/base/meta.pyi +17 -13
  34. reflex/components/base/script.py +60 -58
  35. reflex/components/base/script.pyi +246 -31
  36. reflex/components/base/strict_mode.pyi +5 -4
  37. reflex/components/component.py +109 -194
  38. reflex/components/core/__init__.py +1 -0
  39. reflex/components/core/__init__.pyi +1 -0
  40. reflex/components/core/auto_scroll.pyi +5 -4
  41. reflex/components/core/banner.pyi +25 -19
  42. reflex/components/core/client_side_routing.py +7 -6
  43. reflex/components/core/client_side_routing.pyi +6 -56
  44. reflex/components/core/clipboard.pyi +5 -4
  45. reflex/components/core/debounce.py +1 -0
  46. reflex/components/core/debounce.pyi +5 -4
  47. reflex/components/core/foreach.py +3 -2
  48. reflex/components/core/helmet.py +14 -0
  49. reflex/components/{next/base.pyi → core/helmet.pyi} +10 -7
  50. reflex/components/core/html.pyi +5 -4
  51. reflex/components/core/sticky.pyi +17 -13
  52. reflex/components/core/upload.py +2 -1
  53. reflex/components/core/upload.pyi +21 -16
  54. reflex/components/datadisplay/code.pyi +9 -7
  55. reflex/components/datadisplay/dataeditor.pyi +5 -4
  56. reflex/components/datadisplay/shiki_code_block.pyi +13 -10
  57. reflex/components/dynamic.py +4 -5
  58. reflex/components/el/element.pyi +5 -4
  59. reflex/components/el/elements/base.pyi +5 -4
  60. reflex/components/el/elements/forms.pyi +69 -52
  61. reflex/components/el/elements/inline.pyi +113 -85
  62. reflex/components/el/elements/media.pyi +105 -79
  63. reflex/components/el/elements/metadata.pyi +25 -19
  64. reflex/components/el/elements/other.pyi +29 -22
  65. reflex/components/el/elements/scripts.pyi +13 -10
  66. reflex/components/el/elements/sectioning.pyi +61 -46
  67. reflex/components/el/elements/tables.pyi +41 -31
  68. reflex/components/el/elements/typography.pyi +61 -46
  69. reflex/components/field.py +175 -0
  70. reflex/components/gridjs/datatable.py +2 -2
  71. reflex/components/gridjs/datatable.pyi +11 -9
  72. reflex/components/lucide/icon.py +6 -2
  73. reflex/components/lucide/icon.pyi +15 -10
  74. reflex/components/markdown/markdown.pyi +5 -4
  75. reflex/components/moment/moment.pyi +5 -4
  76. reflex/components/plotly/plotly.pyi +19 -10
  77. reflex/components/props.py +376 -27
  78. reflex/components/radix/primitives/accordion.py +8 -1
  79. reflex/components/radix/primitives/accordion.pyi +29 -22
  80. reflex/components/radix/primitives/base.pyi +9 -7
  81. reflex/components/radix/primitives/drawer.pyi +45 -34
  82. reflex/components/radix/primitives/form.pyi +41 -31
  83. reflex/components/radix/primitives/progress.pyi +21 -16
  84. reflex/components/radix/primitives/slider.pyi +21 -16
  85. reflex/components/radix/themes/base.py +3 -3
  86. reflex/components/radix/themes/base.pyi +33 -25
  87. reflex/components/radix/themes/color_mode.pyi +13 -10
  88. reflex/components/radix/themes/components/alert_dialog.pyi +29 -22
  89. reflex/components/radix/themes/components/aspect_ratio.pyi +5 -4
  90. reflex/components/radix/themes/components/avatar.pyi +5 -4
  91. reflex/components/radix/themes/components/badge.pyi +5 -4
  92. reflex/components/radix/themes/components/button.pyi +5 -4
  93. reflex/components/radix/themes/components/callout.pyi +21 -16
  94. reflex/components/radix/themes/components/card.pyi +5 -4
  95. reflex/components/radix/themes/components/checkbox.pyi +13 -10
  96. reflex/components/radix/themes/components/checkbox_cards.pyi +9 -7
  97. reflex/components/radix/themes/components/checkbox_group.pyi +9 -7
  98. reflex/components/radix/themes/components/context_menu.pyi +53 -40
  99. reflex/components/radix/themes/components/data_list.pyi +17 -13
  100. reflex/components/radix/themes/components/dialog.pyi +29 -22
  101. reflex/components/radix/themes/components/dropdown_menu.pyi +33 -25
  102. reflex/components/radix/themes/components/hover_card.pyi +17 -13
  103. reflex/components/radix/themes/components/icon_button.pyi +5 -4
  104. reflex/components/radix/themes/components/inset.pyi +5 -4
  105. reflex/components/radix/themes/components/popover.pyi +17 -13
  106. reflex/components/radix/themes/components/progress.pyi +5 -4
  107. reflex/components/radix/themes/components/radio.pyi +5 -4
  108. reflex/components/radix/themes/components/radio_cards.pyi +9 -7
  109. reflex/components/radix/themes/components/radio_group.pyi +17 -13
  110. reflex/components/radix/themes/components/scroll_area.pyi +5 -4
  111. reflex/components/radix/themes/components/segmented_control.pyi +9 -7
  112. reflex/components/radix/themes/components/select.pyi +37 -28
  113. reflex/components/radix/themes/components/separator.pyi +5 -4
  114. reflex/components/radix/themes/components/skeleton.pyi +5 -4
  115. reflex/components/radix/themes/components/slider.pyi +5 -4
  116. reflex/components/radix/themes/components/spinner.pyi +5 -4
  117. reflex/components/radix/themes/components/switch.pyi +5 -4
  118. reflex/components/radix/themes/components/table.pyi +29 -22
  119. reflex/components/radix/themes/components/tabs.pyi +21 -16
  120. reflex/components/radix/themes/components/text_area.pyi +5 -4
  121. reflex/components/radix/themes/components/text_field.pyi +13 -10
  122. reflex/components/radix/themes/components/tooltip.pyi +5 -4
  123. reflex/components/radix/themes/layout/base.pyi +5 -4
  124. reflex/components/radix/themes/layout/box.pyi +5 -4
  125. reflex/components/radix/themes/layout/center.pyi +5 -4
  126. reflex/components/radix/themes/layout/container.pyi +5 -4
  127. reflex/components/radix/themes/layout/flex.pyi +5 -4
  128. reflex/components/radix/themes/layout/grid.pyi +5 -4
  129. reflex/components/radix/themes/layout/list.pyi +21 -16
  130. reflex/components/radix/themes/layout/section.pyi +5 -4
  131. reflex/components/radix/themes/layout/spacer.pyi +5 -4
  132. reflex/components/radix/themes/layout/stack.pyi +13 -10
  133. reflex/components/radix/themes/typography/blockquote.pyi +5 -4
  134. reflex/components/radix/themes/typography/code.pyi +5 -4
  135. reflex/components/radix/themes/typography/heading.pyi +5 -4
  136. reflex/components/radix/themes/typography/link.py +42 -9
  137. reflex/components/radix/themes/typography/link.pyi +311 -6
  138. reflex/components/radix/themes/typography/text.pyi +29 -22
  139. reflex/components/react_player/audio.pyi +5 -4
  140. reflex/components/react_player/react_player.pyi +5 -4
  141. reflex/components/react_player/video.pyi +5 -4
  142. reflex/components/recharts/cartesian.py +2 -1
  143. reflex/components/recharts/cartesian.pyi +65 -46
  144. reflex/components/recharts/charts.py +4 -2
  145. reflex/components/recharts/charts.pyi +36 -24
  146. reflex/components/recharts/general.pyi +24 -18
  147. reflex/components/recharts/polar.py +8 -4
  148. reflex/components/recharts/polar.pyi +16 -10
  149. reflex/components/recharts/recharts.pyi +9 -7
  150. reflex/components/sonner/toast.py +2 -2
  151. reflex/components/sonner/toast.pyi +7 -6
  152. reflex/config.py +3 -77
  153. reflex/constants/__init__.py +1 -0
  154. reflex/constants/base.py +38 -8
  155. reflex/constants/compiler.py +4 -2
  156. reflex/constants/event.py +1 -0
  157. reflex/constants/installer.py +23 -16
  158. reflex/constants/state.py +2 -0
  159. reflex/custom_components/custom_components.py +0 -14
  160. reflex/environment.py +1 -1
  161. reflex/event.py +178 -81
  162. reflex/experimental/__init__.py +0 -30
  163. reflex/istate/proxy.py +5 -3
  164. reflex/page.py +0 -27
  165. reflex/plugins/__init__.py +3 -2
  166. reflex/plugins/base.py +5 -1
  167. reflex/plugins/shared_tailwind.py +158 -0
  168. reflex/plugins/sitemap.py +206 -0
  169. reflex/plugins/tailwind_v3.py +13 -106
  170. reflex/plugins/tailwind_v4.py +15 -108
  171. reflex/reflex.py +1 -0
  172. reflex/state.py +134 -140
  173. reflex/testing.py +57 -9
  174. reflex/utils/build.py +9 -69
  175. reflex/utils/exec.py +59 -161
  176. reflex/utils/export.py +1 -1
  177. reflex/utils/imports.py +0 -4
  178. reflex/utils/misc.py +28 -0
  179. reflex/utils/prerequisites.py +62 -59
  180. reflex/utils/processes.py +6 -6
  181. reflex/utils/pyi_generator.py +21 -9
  182. reflex/utils/serializers.py +14 -1
  183. reflex/utils/types.py +196 -61
  184. reflex/vars/__init__.py +2 -0
  185. reflex/vars/base.py +367 -134
  186. {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/METADATA +12 -5
  187. {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/RECORD +190 -196
  188. reflex/.templates/web/next.config.js +0 -7
  189. reflex/components/base/head.py +0 -20
  190. reflex/components/base/head.pyi +0 -116
  191. reflex/components/next/__init__.py +0 -10
  192. reflex/components/next/base.py +0 -7
  193. reflex/components/next/image.py +0 -117
  194. reflex/components/next/image.pyi +0 -94
  195. reflex/components/next/link.py +0 -20
  196. reflex/components/next/link.pyi +0 -67
  197. reflex/components/next/video.py +0 -38
  198. reflex/components/next/video.pyi +0 -68
  199. reflex/components/suneditor/__init__.py +0 -5
  200. reflex/components/suneditor/editor.py +0 -269
  201. reflex/components/suneditor/editor.pyi +0 -199
  202. reflex/experimental/layout.py +0 -254
  203. {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/WHEEL +0 -0
  204. {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/entry_points.txt +0 -0
  205. {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/licenses/LICENSE +0 -0
reflex/state.py CHANGED
@@ -13,20 +13,10 @@ import pickle
13
13
  import sys
14
14
  import typing
15
15
  import warnings
16
- from abc import ABC
17
16
  from collections.abc import AsyncIterator, Callable, Sequence
18
17
  from hashlib import md5
19
18
  from types import FunctionType
20
- from typing import (
21
- TYPE_CHECKING,
22
- Any,
23
- BinaryIO,
24
- ClassVar,
25
- TypeVar,
26
- cast,
27
- get_args,
28
- get_type_hints,
29
- )
19
+ from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, TypeVar, cast, get_type_hints
30
20
 
31
21
  import pydantic.v1 as pydantic
32
22
  from pydantic import BaseModel as BaseModelV2
@@ -38,6 +28,7 @@ from typing_extensions import Self
38
28
  import reflex.istate.dynamic
39
29
  from reflex import constants, event
40
30
  from reflex.base import Base
31
+ from reflex.constants.state import FIELD_MARKER
41
32
  from reflex.environment import PerformanceMode, environment
42
33
  from reflex.event import (
43
34
  BACKGROUND_TASK_MARKER,
@@ -68,21 +59,15 @@ from reflex.utils.exceptions import (
68
59
  )
69
60
  from reflex.utils.exceptions import ImmutableStateError as ImmutableStateError
70
61
  from reflex.utils.exec import is_testing_env
71
- from reflex.utils.types import (
72
- _isinstance,
73
- get_origin,
74
- is_union,
75
- true_type_for_pydantic_field,
76
- value_inside_optional,
77
- )
78
- from reflex.vars import VarData
62
+ from reflex.utils.types import _isinstance, is_union, value_inside_optional
63
+ from reflex.vars import Field, VarData, field
79
64
  from reflex.vars.base import (
80
65
  ComputedVar,
81
66
  DynamicRouteVar,
67
+ EvenMoreBasicBaseState,
82
68
  Var,
83
69
  computed_var,
84
70
  dispatch,
85
- get_unique_variable_name,
86
71
  is_computed_var,
87
72
  )
88
73
 
@@ -205,6 +190,21 @@ class EventHandlerSetVar(EventHandler):
205
190
  )
206
191
  object.__setattr__(self, "state_cls", state_cls)
207
192
 
193
+ def __hash__(self):
194
+ """Get the hash of the event handler.
195
+
196
+ Returns:
197
+ The hash of the event handler.
198
+ """
199
+ return hash(
200
+ (
201
+ tuple(self.event_actions.items()),
202
+ self.fn,
203
+ self.state_full_name,
204
+ self.state_cls,
205
+ )
206
+ )
207
+
208
208
  def setvar(self, var_name: str, value: Any):
209
209
  """Set the state variable to the value of the event.
210
210
 
@@ -255,38 +255,25 @@ if TYPE_CHECKING:
255
255
  from pydantic.v1.fields import ModelField
256
256
 
257
257
 
258
- def _unwrap_field_type(type_: types.GenericType) -> type:
259
- """Unwrap rx.Field type annotations.
260
-
261
- Args:
262
- type_: The type to unwrap.
263
-
264
- Returns:
265
- The unwrapped type.
266
- """
267
- from reflex.vars import Field
268
-
269
- if get_origin(type_) is Field:
270
- return get_args(type_)[0]
271
- return type_
272
-
273
-
274
- def get_var_for_field(cls: type[BaseState], f: ModelField):
275
- """Get a Var instance for a Pydantic field.
258
+ def get_var_for_field(cls: type[BaseState], name: str, f: Field) -> Var:
259
+ """Get a Var instance for a state field.
276
260
 
277
261
  Args:
278
262
  cls: The state class.
279
- f: The Pydantic field.
263
+ name: The name of the field.
264
+ f: The Field instance.
280
265
 
281
266
  Returns:
282
267
  The Var instance.
283
268
  """
284
- field_name = format.format_state_name(cls.get_full_name()) + "." + f.name
269
+ field_name = (
270
+ format.format_state_name(cls.get_full_name()) + "." + name + FIELD_MARKER
271
+ )
285
272
 
286
273
  return dispatch(
287
274
  field_name=field_name,
288
- var_data=VarData.from_state(cls, f.name),
289
- result_var_type=_unwrap_field_type(true_type_for_pydantic_field(f)),
275
+ var_data=VarData.from_state(cls, name),
276
+ result_var_type=f.outer_type_,
290
277
  )
291
278
 
292
279
 
@@ -312,7 +299,7 @@ async def _resolve_delta(delta: Delta) -> Delta:
312
299
  all_base_state_classes: dict[str, None] = {}
313
300
 
314
301
 
315
- class BaseState(Base, ABC, extra=pydantic.Extra.allow):
302
+ class BaseState(EvenMoreBasicBaseState):
316
303
  """The state of the app."""
317
304
 
318
305
  # A map from the var name to the var.
@@ -352,31 +339,34 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
352
339
  _potentially_dirty_states: ClassVar[set[str]] = set()
353
340
 
354
341
  # The parent state.
355
- parent_state: BaseState | None = None
342
+ parent_state: BaseState | None = field(default=None, is_var=False)
356
343
 
357
344
  # The substates of the state.
358
- substates: builtins.dict[str, BaseState] = {}
345
+ substates: builtins.dict[str, BaseState] = field(
346
+ default_factory=builtins.dict, is_var=False
347
+ )
359
348
 
360
349
  # The set of dirty vars.
361
- dirty_vars: set[str] = set()
350
+ dirty_vars: set[str] = field(default_factory=set, is_var=False)
362
351
 
363
352
  # The set of dirty substates.
364
- dirty_substates: set[str] = set()
353
+ dirty_substates: set[str] = field(default_factory=set, is_var=False)
365
354
 
366
355
  # The routing path that triggered the state
367
- router_data: builtins.dict[str, Any] = {}
356
+ router_data: builtins.dict[str, Any] = field(
357
+ default_factory=builtins.dict, is_var=False
358
+ )
368
359
 
369
360
  # Per-instance copy of backend base variable values
370
- _backend_vars: builtins.dict[str, Any] = {}
361
+ _backend_vars: builtins.dict[str, Any] = field(
362
+ default_factory=builtins.dict, is_var=False
363
+ )
371
364
 
372
365
  # The router data for the current page
373
- router: RouterData = RouterData()
366
+ router: Field[RouterData] = field(default_factory=RouterData)
374
367
 
375
368
  # Whether the state has ever been touched since instantiation.
376
- _was_touched: bool = False
377
-
378
- # Whether this state class is a mixin and should not be instantiated.
379
- _mixin: ClassVar[bool] = False
369
+ _was_touched: bool = field(default=False, is_var=False)
380
370
 
381
371
  # A special event handler for setting base vars.
382
372
  setvar: ClassVar[EventHandler]
@@ -409,13 +399,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
409
399
  "See https://reflex.dev/docs/state/ for further information."
410
400
  )
411
401
  raise ReflexRuntimeError(msg)
412
- if type(self)._mixin:
402
+ if self._mixin:
413
403
  msg = f"{type(self).__name__} is a state mixin and cannot be instantiated directly."
414
404
  raise ReflexRuntimeError(msg)
415
405
  kwargs["parent_state"] = parent_state
416
- super().__init__()
417
- for name, value in kwargs.items():
418
- setattr(self, name, value)
406
+ super().__init__(**kwargs)
419
407
 
420
408
  # Setup the substates (for memory state manager only).
421
409
  if init_substates:
@@ -437,14 +425,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
437
425
  return f"{type(self).__name__}({self.dict()})"
438
426
 
439
427
  @classmethod
440
- def _get_computed_vars(cls) -> list[ComputedVar]:
428
+ def _get_computed_vars(cls) -> list[tuple[str, ComputedVar]]:
441
429
  """Helper function to get all computed vars of a instance.
442
430
 
443
431
  Returns:
444
432
  A list of computed vars.
445
433
  """
446
434
  return [
447
- v
435
+ (name, v)
448
436
  for mixin in [*cls._mixins(), cls]
449
437
  for name, v in mixin.__dict__.items()
450
438
  if is_computed_var(v) and name not in cls.inherited_vars
@@ -481,8 +469,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
481
469
 
482
470
  super().__init_subclass__(**kwargs)
483
471
 
484
- cls._mixin = mixin
485
- if mixin:
472
+ if cls._mixin:
486
473
  return
487
474
 
488
475
  # Handle locally-defined states for pickling.
@@ -548,13 +535,13 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
548
535
 
549
536
  # Set the base and computed vars.
550
537
  cls.base_vars = {
551
- f.name: get_var_for_field(cls, f)
552
- for f in cls.get_fields().values()
553
- if f.name not in cls.get_skip_vars()
538
+ name: get_var_for_field(cls, name, f)
539
+ for name, f in cls.get_fields().items()
540
+ if name not in cls.get_skip_vars() and f.is_var and not name.startswith("_")
554
541
  }
555
542
  cls.computed_vars = {
556
- v._js_expr: v._replace(merge_var_data=VarData.from_state(cls))
557
- for v in computed_vars
543
+ name: v._replace(merge_var_data=VarData.from_state(cls))
544
+ for name, v in computed_vars
558
545
  }
559
546
  cls.vars = {
560
547
  **cls.inherited_vars,
@@ -564,8 +551,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
564
551
  cls.event_handlers = {}
565
552
 
566
553
  # Setup the base vars at the class level.
567
- for prop in cls.base_vars.values():
568
- cls._init_var(prop)
554
+ for name, prop in cls.base_vars.items():
555
+ cls._init_var(name, prop)
569
556
 
570
557
  # Set up the event handlers.
571
558
  events = {
@@ -583,8 +570,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
583
570
  newcv = value._replace(fget=fget, _var_data=VarData.from_state(cls))
584
571
  # cleanup refs to mixin cls in var_data
585
572
  setattr(cls, name, newcv)
586
- cls.computed_vars[newcv._js_expr] = newcv
587
- cls.vars[newcv._js_expr] = newcv
573
+ cls.computed_vars[name] = newcv
574
+ cls.vars[name] = newcv
588
575
  continue
589
576
  if types.is_backend_base_variable(name, mixin_cls):
590
577
  cls.backend_vars[name] = copy.deepcopy(value)
@@ -687,9 +674,16 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
687
674
 
688
675
  of_type = of_type or Component
689
676
 
690
- unique_var_name = get_unique_variable_name()
677
+ unique_var_name = (
678
+ ("dynamic_" + f.__module__ + "_" + f.__qualname__)
679
+ .replace("<", "")
680
+ .replace(">", "")
681
+ .replace(".", "_")
682
+ )
683
+
684
+ while unique_var_name in cls.vars:
685
+ unique_var_name += "_"
691
686
 
692
- @computed_var(_js_expr=unique_var_name, return_type=of_type)
693
687
  def computed_var_func(state: Self):
694
688
  result = f(state)
695
689
 
@@ -701,10 +695,16 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
701
695
 
702
696
  return result
703
697
 
704
- setattr(cls, unique_var_name, computed_var_func)
705
- cls.computed_vars[unique_var_name] = computed_var_func
706
- cls.vars[unique_var_name] = computed_var_func
707
- cls._update_substate_inherited_vars({unique_var_name: computed_var_func})
698
+ computed_var_func.__name__ = unique_var_name
699
+
700
+ computed_var_func_arg = computed_var(return_type=of_type, cache=False)(
701
+ computed_var_func
702
+ )
703
+
704
+ setattr(cls, unique_var_name, computed_var_func_arg)
705
+ cls.computed_vars[unique_var_name] = computed_var_func_arg
706
+ cls.vars[unique_var_name] = computed_var_func_arg
707
+ cls._update_substate_inherited_vars({unique_var_name: computed_var_func_arg})
708
708
  cls._always_dirty_computed_vars.add(unique_var_name)
709
709
 
710
710
  return getattr(cls, unique_var_name)
@@ -842,8 +842,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
842
842
  Raises:
843
843
  ComputedVarShadowsBaseVarsError: When a computed var shadows a base var.
844
844
  """
845
- for computed_var_ in cls._get_computed_vars():
846
- if computed_var_._js_expr in cls.__annotations__:
845
+ for name, computed_var_ in cls._get_computed_vars():
846
+ if name in cls.__annotations__:
847
847
  msg = f"The computed var name `{computed_var_._js_expr}` shadows a base var in {cls.__module__}.{cls.__name__}; use a different name instead"
848
848
  raise ComputedVarShadowsBaseVarsError(msg)
849
849
 
@@ -898,7 +898,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
898
898
  if issubclass(base, BaseState) and base is not BaseState and not base._mixin
899
899
  ]
900
900
  if len(parent_states) >= 2:
901
- msg = f"Only one parent state is allowed {parent_states}."
901
+ msg = f"Only one parent state of is allowed. Found {parent_states} parents of {cls}."
902
902
  raise ValueError(msg)
903
903
  # The first non-mixin state in the mro is our parent.
904
904
  for base in cls.mro()[1:]:
@@ -1016,10 +1016,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1016
1016
  )
1017
1017
 
1018
1018
  @classmethod
1019
- def _init_var(cls, prop: Var):
1019
+ def _init_var(cls, name: str, prop: Var):
1020
1020
  """Initialize a variable.
1021
1021
 
1022
1022
  Args:
1023
+ name: The name of the variable
1023
1024
  prop: The variable to initialize
1024
1025
 
1025
1026
  Raises:
@@ -1036,10 +1037,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1036
1037
  f'Found var "{prop._js_expr}" with type {prop._var_type}.'
1037
1038
  )
1038
1039
  raise VarTypeError(msg)
1039
- cls._set_var(prop)
1040
+ cls._set_var(name, prop)
1040
1041
  if cls.is_user_defined() and get_config().state_auto_setters:
1041
- cls._create_setter(prop)
1042
- cls._set_default_value(prop)
1042
+ cls._create_setter(name, prop)
1043
+ cls._set_default_value(name, prop)
1043
1044
 
1044
1045
  @classmethod
1045
1046
  def add_var(cls, name: str, type_: Any, default_value: Any = None):
@@ -1062,15 +1063,18 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1062
1063
 
1063
1064
  # create the variable based on name and type
1064
1065
  var = Var(
1065
- _js_expr=format.format_state_name(cls.get_full_name()) + "." + name,
1066
+ _js_expr=format.format_state_name(cls.get_full_name())
1067
+ + "."
1068
+ + name
1069
+ + FIELD_MARKER,
1066
1070
  _var_type=type_,
1067
1071
  _var_data=VarData.from_state(cls, name),
1068
1072
  ).guess_type()
1069
1073
 
1070
1074
  # add the pydantic field dynamically (must be done before _init_var)
1071
- cls.add_field(var, default_value)
1075
+ cls.add_field(name, var, default_value)
1072
1076
 
1073
- cls._init_var(var)
1077
+ cls._init_var(name, var)
1074
1078
 
1075
1079
  # update the internal dicts so the new variable is correctly handled
1076
1080
  cls.base_vars.update({name: var})
@@ -1084,13 +1088,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1084
1088
  cls._init_var_dependency_dicts()
1085
1089
 
1086
1090
  @classmethod
1087
- def _set_var(cls, prop: Var):
1091
+ def _set_var(cls, name: str, prop: Var):
1088
1092
  """Set the var as a class member.
1089
1093
 
1090
1094
  Args:
1095
+ name: The name of the var.
1091
1096
  prop: The var instance to set.
1092
1097
  """
1093
- setattr(cls, prop._var_field_name, prop)
1098
+ setattr(cls, name, prop)
1094
1099
 
1095
1100
  @classmethod
1096
1101
  def _create_event_handler(cls, fn: Any):
@@ -1110,38 +1115,31 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1110
1115
  cls.setvar = cls.event_handlers["setvar"] = EventHandlerSetVar(state_cls=cls)
1111
1116
 
1112
1117
  @classmethod
1113
- def _create_setter(cls, prop: Var):
1118
+ def _create_setter(cls, name: str, prop: Var):
1114
1119
  """Create a setter for the var.
1115
1120
 
1116
1121
  Args:
1122
+ name: The name of the var.
1117
1123
  prop: The var to create a setter for.
1118
1124
  """
1119
- setter_name = prop._get_setter_name(include_state=False)
1125
+ setter_name = Var._get_setter_name_for_name(name)
1120
1126
  if setter_name not in cls.__dict__:
1121
- event_handler = cls._create_event_handler(prop._get_setter())
1127
+ event_handler = cls._create_event_handler(prop._get_setter(name))
1122
1128
  cls.event_handlers[setter_name] = event_handler
1123
1129
  setattr(cls, setter_name, event_handler)
1124
1130
 
1125
1131
  @classmethod
1126
- def _set_default_value(cls, prop: Var):
1132
+ def _set_default_value(cls, name: str, prop: Var):
1127
1133
  """Set the default value for the var.
1128
1134
 
1129
1135
  Args:
1136
+ name: The name of the var.
1130
1137
  prop: The var to set the default value for.
1131
1138
  """
1132
1139
  # Get the pydantic field for the var.
1133
- field = cls.get_fields()[prop._var_field_name]
1134
- if field.required:
1135
- default_value = prop._get_default_value()
1136
- if default_value is not None:
1137
- field.required = False
1138
- field.default = default_value
1139
- if (
1140
- not field.required
1141
- and field.default is None
1142
- and field.default_factory is None
1143
- and not types.is_optional(prop._var_type)
1144
- ):
1140
+ field = cls.get_fields()[name]
1141
+
1142
+ if field.default is None and not types.is_optional(prop._var_type):
1145
1143
  # Ensure frontend uses null coalescing when accessing.
1146
1144
  object.__setattr__(prop, "_var_type", prop._var_type | None)
1147
1145
 
@@ -1160,7 +1158,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1160
1158
  return getattr(cls, name)
1161
1159
  except AttributeError:
1162
1160
  try:
1163
- return Var("", _var_type=annotation_value)._get_default_value()
1161
+ return types.get_default_value_for_type(annotation_value)
1164
1162
  except TypeError:
1165
1163
  pass
1166
1164
  return None
@@ -1215,12 +1213,16 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1215
1213
  def inner_func(self: BaseState) -> str:
1216
1214
  return self.router.page.params.get(param, "")
1217
1215
 
1216
+ inner_func.__name__ = param
1217
+
1218
1218
  return inner_func
1219
1219
 
1220
1220
  def arglist_factory(param: str):
1221
1221
  def inner_func(self: BaseState) -> list[str]:
1222
1222
  return self.router.page.params.get(param, [])
1223
1223
 
1224
+ inner_func.__name__ = param
1225
+
1224
1226
  return inner_func
1225
1227
 
1226
1228
  dynamic_vars = {}
@@ -1235,8 +1237,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1235
1237
  fget=func,
1236
1238
  auto_deps=False,
1237
1239
  deps=["router"],
1238
- _js_expr=param,
1239
- _var_data=VarData.from_state(cls),
1240
+ _var_data=VarData.from_state(cls, param),
1240
1241
  )
1241
1242
  setattr(cls, param, dynamic_vars[param])
1242
1243
 
@@ -1276,10 +1277,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1276
1277
  Returns:
1277
1278
  The value of the var.
1278
1279
  """
1279
- # If the state hasn't been initialized yet, return the default value.
1280
- if not super().__getattribute__("__dict__"):
1281
- return super().__getattribute__(name)
1282
-
1283
1280
  # Fast path for dunder
1284
1281
  if name.startswith("__"):
1285
1282
  return super().__getattribute__(name)
@@ -1306,7 +1303,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1306
1303
  fn.__qualname__ = handler.fn.__qualname__
1307
1304
  return fn
1308
1305
 
1309
- backend_vars = super().__getattribute__("_backend_vars")
1306
+ backend_vars = super().__getattribute__("_backend_vars") or {}
1310
1307
  if name in backend_vars:
1311
1308
  value = backend_vars[name]
1312
1309
  else:
@@ -1370,9 +1367,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1370
1367
 
1371
1368
  fields = self.get_fields()
1372
1369
 
1373
- if name in fields:
1374
- field = fields[name]
1375
- field_type = _unwrap_field_type(true_type_for_pydantic_field(field))
1370
+ if (field := fields.get(name)) is not None and field.is_var:
1371
+ field_type = field.outer_type_
1376
1372
  if not _isinstance(value, field_type, nested=1, treat_var_as_type=False):
1377
1373
  console.error(
1378
1374
  f"Expected field '{type(self).__name__}.{name}' to receive type '{escape(str(field_type))}',"
@@ -1380,7 +1376,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1380
1376
  )
1381
1377
 
1382
1378
  # Set the attribute.
1383
- super().__setattr__(name, value)
1379
+ object.__setattr__(self, name, value)
1384
1380
 
1385
1381
  # Add the var to the dirty list.
1386
1382
  if name in self.base_vars:
@@ -1969,7 +1965,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1969
1965
  )
1970
1966
 
1971
1967
  subdelta: dict[str, Any] = {
1972
- prop: self.get_value(prop)
1968
+ prop + FIELD_MARKER: self.get_value(prop)
1973
1969
  for prop in delta_vars
1974
1970
  if not types.is_backend_base_variable(prop, type(self))
1975
1971
  }
@@ -2058,7 +2054,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2058
2054
  Returns:
2059
2055
  The value of the field.
2060
2056
  """
2061
- value = super().get_value(key)
2057
+ value = getattr(self, key)
2062
2058
  if isinstance(value, MutableProxy):
2063
2059
  return value.__wrapped__
2064
2060
  return value
@@ -2104,7 +2100,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2104
2100
  computed_vars = {}
2105
2101
  variables = {**base_vars, **computed_vars}
2106
2102
  d = {
2107
- self.get_full_name(): {k: variables[k] for k in sorted(variables)},
2103
+ self.get_full_name(): {
2104
+ k + FIELD_MARKER: variables[k] for k in sorted(variables)
2105
+ },
2108
2106
  }
2109
2107
  for substate_d in [
2110
2108
  v.dict(include_computed=include_computed, initial=initial, **kwargs)
@@ -2147,19 +2145,19 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2147
2145
  Returns:
2148
2146
  The state dict for serialization.
2149
2147
  """
2150
- state = super().__getstate__()
2151
- state["__dict__"] = state["__dict__"].copy()
2152
- if state["__dict__"].get("parent_state") is not None:
2148
+ state = self.__dict__
2149
+ state = state.copy()
2150
+ if state.get("parent_state") is not None:
2153
2151
  # Do not serialize router data in substates (only the root state).
2154
- state["__dict__"].pop("router", None)
2155
- state["__dict__"].pop("router_data", None)
2152
+ state.pop("router", None)
2153
+ state.pop("router_data", None)
2156
2154
  # Never serialize parent_state or substates.
2157
- state["__dict__"].pop("parent_state", None)
2158
- state["__dict__"].pop("substates", None)
2159
- state["__dict__"].pop("_was_touched", None)
2155
+ state.pop("parent_state", None)
2156
+ state.pop("substates", None)
2157
+ state.pop("_was_touched", None)
2160
2158
  # Remove all inherited vars.
2161
2159
  for inherited_var_name in self.inherited_vars:
2162
- state["__dict__"].pop(inherited_var_name, None)
2160
+ state.pop(inherited_var_name, None)
2163
2161
  return state
2164
2162
 
2165
2163
  def __setstate__(self, state: dict[str, Any]):
@@ -2170,9 +2168,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2170
2168
  Args:
2171
2169
  state: The state dict for deserialization.
2172
2170
  """
2173
- state["__dict__"]["parent_state"] = None
2174
- state["__dict__"]["substates"] = {}
2175
- super().__setstate__(state)
2171
+ state["parent_state"] = None
2172
+ state["substates"] = {}
2173
+ for key, value in state.items():
2174
+ object.__setattr__(self, key, value)
2176
2175
 
2177
2176
  def _check_state_size(
2178
2177
  self,
@@ -2213,17 +2212,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
2213
2212
 
2214
2213
  def _field_tuple(
2215
2214
  field_name: str,
2216
- ) -> tuple[str, str, Any, bool | None, Any]:
2215
+ ) -> tuple[str, Any, Any]:
2217
2216
  model_field = cls.__fields__[field_name]
2218
2217
  return (
2219
2218
  field_name,
2220
- model_field.name,
2221
2219
  _serialize_type(model_field.type_),
2222
- (
2223
- model_field.required
2224
- if isinstance(model_field.required, bool)
2225
- else None
2226
- ),
2227
2220
  (model_field.default if is_serializable(model_field.default) else None),
2228
2221
  )
2229
2222
 
@@ -2430,6 +2423,7 @@ class UpdateVarsInternalState(State):
2430
2423
  """
2431
2424
  for var, value in vars.items():
2432
2425
  state_name, _, var_name = var.rpartition(".")
2426
+ var_name = var_name.removesuffix(FIELD_MARKER)
2433
2427
  var_state_cls = State.get_class_substate(state_name)
2434
2428
  if var_state_cls._is_client_storage(var_name):
2435
2429
  var_state = await self.get_state(var_state_cls)
@@ -2518,7 +2512,7 @@ class ComponentState(State, mixin=True):
2518
2512
  Raises:
2519
2513
  ReflexRuntimeError: If the ComponentState is initialized directly.
2520
2514
  """
2521
- if type(self)._mixin:
2515
+ if self._mixin:
2522
2516
  raise ReflexRuntimeError(
2523
2517
  f"{ComponentState.__name__} {type(self).__name__} is not meant to be initialized directly. "
2524
2518
  + "Use the `create` method to create a new instance and access the state via the `State` attribute."