reflex 0.6.3.post1__py3-none-any.whl → 0.6.4__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 (96) hide show
  1. reflex/.templates/jinja/web/pages/_app.js.jinja2 +2 -2
  2. reflex/.templates/jinja/web/utils/context.js.jinja2 +3 -1
  3. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +3 -3
  4. reflex/.templates/web/components/shiki/code.js +29 -0
  5. reflex/.templates/web/jsconfig.json +2 -1
  6. reflex/.templates/web/utils/state.js +6 -4
  7. reflex/__init__.py +6 -3
  8. reflex/__init__.pyi +4 -3
  9. reflex/app.py +6 -5
  10. reflex/compiler/compiler.py +6 -7
  11. reflex/compiler/utils.py +8 -1
  12. reflex/components/base/error_boundary.py +37 -24
  13. reflex/components/base/error_boundary.pyi +8 -7
  14. reflex/components/component.py +9 -4
  15. reflex/components/core/banner.py +2 -2
  16. reflex/components/core/client_side_routing.py +1 -1
  17. reflex/components/core/clipboard.py +1 -1
  18. reflex/components/core/clipboard.pyi +1 -1
  19. reflex/components/core/cond.py +1 -1
  20. reflex/components/core/debounce.py +5 -1
  21. reflex/components/core/upload.py +7 -9
  22. reflex/components/core/upload.pyi +2 -2
  23. reflex/components/datadisplay/code.py +1 -1
  24. reflex/components/datadisplay/dataeditor.py +83 -18
  25. reflex/components/datadisplay/dataeditor.pyi +67 -15
  26. reflex/components/datadisplay/shiki_code_block.py +813 -0
  27. reflex/components/datadisplay/shiki_code_block.pyi +2211 -0
  28. reflex/components/dynamic.py +3 -3
  29. reflex/components/el/elements/forms.py +37 -23
  30. reflex/components/el/elements/forms.pyi +7 -4
  31. reflex/components/markdown/markdown.py +12 -1
  32. reflex/components/moment/moment.pyi +1 -1
  33. reflex/components/radix/primitives/drawer.pyi +2 -2
  34. reflex/components/radix/themes/base.py +2 -2
  35. reflex/components/radix/themes/color_mode.pyi +1 -1
  36. reflex/components/radix/themes/components/alert_dialog.pyi +1 -1
  37. reflex/components/radix/themes/components/checkbox.pyi +3 -3
  38. reflex/components/radix/themes/components/context_menu.pyi +1 -1
  39. reflex/components/radix/themes/components/dialog.pyi +2 -2
  40. reflex/components/radix/themes/components/dropdown_menu.pyi +2 -2
  41. reflex/components/radix/themes/components/hover_card.pyi +2 -2
  42. reflex/components/radix/themes/components/popover.pyi +1 -1
  43. reflex/components/radix/themes/components/radio_cards.pyi +1 -1
  44. reflex/components/radix/themes/components/radio_group.pyi +1 -1
  45. reflex/components/radix/themes/components/select.pyi +6 -6
  46. reflex/components/radix/themes/components/switch.pyi +1 -1
  47. reflex/components/radix/themes/components/tabs.pyi +2 -2
  48. reflex/components/radix/themes/components/tooltip.pyi +4 -2
  49. reflex/components/react_player/__init__.py +1 -0
  50. reflex/components/react_player/audio.pyi +6 -3
  51. reflex/components/react_player/react_player.py +12 -1
  52. reflex/components/react_player/react_player.pyi +11 -3
  53. reflex/components/react_player/video.pyi +6 -3
  54. reflex/components/recharts/recharts.py +2 -2
  55. reflex/components/sonner/toast.py +1 -1
  56. reflex/components/suneditor/editor.py +40 -16
  57. reflex/components/suneditor/editor.pyi +15 -11
  58. reflex/config.py +284 -20
  59. reflex/constants/__init__.py +2 -0
  60. reflex/constants/base.py +53 -31
  61. reflex/constants/compiler.py +2 -12
  62. reflex/constants/config.py +1 -2
  63. reflex/constants/installer.py +88 -32
  64. reflex/constants/style.py +1 -1
  65. reflex/constants/utils.py +32 -0
  66. reflex/custom_components/custom_components.py +3 -3
  67. reflex/event.py +152 -84
  68. reflex/experimental/__init__.py +2 -0
  69. reflex/experimental/client_state.py +1 -1
  70. reflex/experimental/layout.pyi +1 -1
  71. reflex/istate/storage.py +144 -0
  72. reflex/model.py +8 -11
  73. reflex/reflex.py +18 -17
  74. reflex/state.py +83 -149
  75. reflex/style.py +1 -1
  76. reflex/testing.py +2 -1
  77. reflex/utils/build.py +0 -12
  78. reflex/utils/exceptions.py +8 -0
  79. reflex/utils/exec.py +22 -4
  80. reflex/utils/imports.py +6 -0
  81. reflex/utils/net.py +2 -4
  82. reflex/utils/path_ops.py +7 -21
  83. reflex/utils/prerequisites.py +11 -17
  84. reflex/utils/pyi_generator.py +91 -3
  85. reflex/utils/registry.py +2 -6
  86. reflex/utils/types.py +14 -0
  87. reflex/vars/base.py +453 -424
  88. reflex/vars/function.py +6 -16
  89. reflex/vars/number.py +46 -67
  90. reflex/vars/object.py +1 -31
  91. reflex/vars/sequence.py +177 -47
  92. {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/METADATA +1 -1
  93. {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/RECORD +96 -91
  94. {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/LICENSE +0 -0
  95. {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/WHEEL +0 -0
  96. {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/entry_points.txt +0 -0
reflex/vars/base.py CHANGED
@@ -19,6 +19,7 @@ from typing import (
19
19
  TYPE_CHECKING,
20
20
  Any,
21
21
  Callable,
22
+ ClassVar,
22
23
  Dict,
23
24
  FrozenSet,
24
25
  Generic,
@@ -37,7 +38,13 @@ from typing import (
37
38
  overload,
38
39
  )
39
40
 
40
- from typing_extensions import ParamSpec, TypeGuard, deprecated, get_type_hints, override
41
+ from typing_extensions import (
42
+ ParamSpec,
43
+ TypeGuard,
44
+ deprecated,
45
+ get_type_hints,
46
+ override,
47
+ )
41
48
 
42
49
  from reflex import constants
43
50
  from reflex.base import Base
@@ -61,15 +68,13 @@ from reflex.utils.types import GenericType, Self, get_origin, has_args, unionize
61
68
  if TYPE_CHECKING:
62
69
  from reflex.state import BaseState
63
70
 
64
- from .function import FunctionVar, ToFunctionOperation
71
+ from .function import FunctionVar
65
72
  from .number import (
66
73
  BooleanVar,
67
74
  NumberVar,
68
- ToBooleanVarOperation,
69
- ToNumberVarOperation,
70
75
  )
71
- from .object import ObjectVar, ToObjectOperation
72
- from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
76
+ from .object import ObjectVar
77
+ from .sequence import ArrayVar, StringVar
73
78
 
74
79
 
75
80
  VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
@@ -78,6 +83,184 @@ OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE")
78
83
  warnings.filterwarnings("ignore", message="fields may not start with an underscore")
79
84
 
80
85
 
86
+ @dataclasses.dataclass(
87
+ eq=False,
88
+ frozen=True,
89
+ )
90
+ class VarSubclassEntry:
91
+ """Entry for a Var subclass."""
92
+
93
+ var_subclass: Type[Var]
94
+ to_var_subclass: Type[ToOperation]
95
+ python_types: Tuple[GenericType, ...]
96
+
97
+
98
+ _var_subclasses: List[VarSubclassEntry] = []
99
+ _var_literal_subclasses: List[Tuple[Type[LiteralVar], VarSubclassEntry]] = []
100
+
101
+
102
+ @dataclasses.dataclass(
103
+ eq=True,
104
+ frozen=True,
105
+ )
106
+ class VarData:
107
+ """Metadata associated with a x."""
108
+
109
+ # The name of the enclosing state.
110
+ state: str = dataclasses.field(default="")
111
+
112
+ # The name of the field in the state.
113
+ field_name: str = dataclasses.field(default="")
114
+
115
+ # Imports needed to render this var
116
+ imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
117
+
118
+ # Hooks that need to be present in the component to render this var
119
+ hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
120
+
121
+ def __init__(
122
+ self,
123
+ state: str = "",
124
+ field_name: str = "",
125
+ imports: ImportDict | ParsedImportDict | None = None,
126
+ hooks: dict[str, None] | None = None,
127
+ ):
128
+ """Initialize the var data.
129
+
130
+ Args:
131
+ state: The name of the enclosing state.
132
+ field_name: The name of the field in the state.
133
+ imports: Imports needed to render this var.
134
+ hooks: Hooks that need to be present in the component to render this var.
135
+ """
136
+ immutable_imports: ImmutableParsedImportDict = tuple(
137
+ sorted(
138
+ ((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
139
+ )
140
+ )
141
+ object.__setattr__(self, "state", state)
142
+ object.__setattr__(self, "field_name", field_name)
143
+ object.__setattr__(self, "imports", immutable_imports)
144
+ object.__setattr__(self, "hooks", tuple(hooks or {}))
145
+
146
+ def old_school_imports(self) -> ImportDict:
147
+ """Return the imports as a mutable dict.
148
+
149
+ Returns:
150
+ The imports as a mutable dict.
151
+ """
152
+ return dict((k, list(v)) for k, v in self.imports)
153
+
154
+ @classmethod
155
+ def merge(cls, *others: VarData | None) -> VarData | None:
156
+ """Merge multiple var data objects.
157
+
158
+ Args:
159
+ *others: The var data objects to merge.
160
+
161
+ Returns:
162
+ The merged var data object.
163
+ """
164
+ state = ""
165
+ field_name = ""
166
+ _imports = {}
167
+ hooks = {}
168
+ for var_data in others:
169
+ if var_data is None:
170
+ continue
171
+ state = state or var_data.state
172
+ field_name = field_name or var_data.field_name
173
+ _imports = imports.merge_imports(_imports, var_data.imports)
174
+ hooks.update(
175
+ var_data.hooks
176
+ if isinstance(var_data.hooks, dict)
177
+ else {k: None for k in var_data.hooks}
178
+ )
179
+
180
+ if state or _imports or hooks or field_name:
181
+ return VarData(
182
+ state=state,
183
+ field_name=field_name,
184
+ imports=_imports,
185
+ hooks=hooks,
186
+ )
187
+ return None
188
+
189
+ def __bool__(self) -> bool:
190
+ """Check if the var data is non-empty.
191
+
192
+ Returns:
193
+ True if any field is set to a non-default value.
194
+ """
195
+ return bool(self.state or self.imports or self.hooks or self.field_name)
196
+
197
+ @classmethod
198
+ def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData:
199
+ """Set the state of the var.
200
+
201
+ Args:
202
+ state: The state to set or the full name of the state.
203
+ field_name: The name of the field in the state. Optional.
204
+
205
+ Returns:
206
+ The var with the set state.
207
+ """
208
+ from reflex.utils import format
209
+
210
+ state_name = state if isinstance(state, str) else state.get_full_name()
211
+ return VarData(
212
+ state=state_name,
213
+ field_name=field_name,
214
+ hooks={
215
+ "const {0} = useContext(StateContexts.{0})".format(
216
+ format.format_state_name(state_name)
217
+ ): None
218
+ },
219
+ imports={
220
+ f"$/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
221
+ "react": [ImportVar(tag="useContext")],
222
+ },
223
+ )
224
+
225
+
226
+ def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
227
+ """Decode the state name from a formatted var.
228
+
229
+ Args:
230
+ value: The value to extract the state name from.
231
+
232
+ Returns:
233
+ The extracted state name and the value without the state name.
234
+ """
235
+ var_datas = []
236
+ if isinstance(value, str):
237
+ # fast path if there is no encoded VarData
238
+ if constants.REFLEX_VAR_OPENING_TAG not in value:
239
+ return None, value
240
+
241
+ offset = 0
242
+
243
+ # Find all tags.
244
+ while m := _decode_var_pattern.search(value):
245
+ start, end = m.span()
246
+ value = value[:start] + value[end:]
247
+
248
+ serialized_data = m.group(1)
249
+
250
+ if serialized_data.isnumeric() or (
251
+ serialized_data[0] == "-" and serialized_data[1:].isnumeric()
252
+ ):
253
+ # This is a global immutable var.
254
+ var = _global_vars[int(serialized_data)]
255
+ var_data = var._get_all_var_data()
256
+
257
+ if var_data is not None:
258
+ var_datas.append(var_data)
259
+ offset += end - start
260
+
261
+ return VarData.merge(*var_datas) if var_datas else None, value
262
+
263
+
81
264
  @dataclasses.dataclass(
82
265
  eq=False,
83
266
  frozen=True,
@@ -151,6 +334,40 @@ class Var(Generic[VAR_TYPE]):
151
334
  """
152
335
  return False
153
336
 
337
+ def __init_subclass__(
338
+ cls, python_types: Tuple[GenericType, ...] | GenericType = types.Unset, **kwargs
339
+ ):
340
+ """Initialize the subclass.
341
+
342
+ Args:
343
+ python_types: The python types that the var represents.
344
+ **kwargs: Additional keyword arguments.
345
+ """
346
+ super().__init_subclass__(**kwargs)
347
+
348
+ if python_types is not types.Unset:
349
+ python_types = (
350
+ python_types if isinstance(python_types, tuple) else (python_types,)
351
+ )
352
+
353
+ @dataclasses.dataclass(
354
+ eq=False,
355
+ frozen=True,
356
+ **{"slots": True} if sys.version_info >= (3, 10) else {},
357
+ )
358
+ class ToVarOperation(ToOperation, cls):
359
+ """Base class of converting a var to another var type."""
360
+
361
+ _original: Var = dataclasses.field(
362
+ default=Var(_js_expr="null", _var_type=None),
363
+ )
364
+
365
+ _default_var_type: ClassVar[GenericType] = python_types[0]
366
+
367
+ ToVarOperation.__name__ = f'To{cls.__name__.removesuffix("Var")}Operation'
368
+
369
+ _var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types))
370
+
154
371
  def __post_init__(self):
155
372
  """Post-initialize the var."""
156
373
  # Decode any inline Var markup and apply it to the instance
@@ -331,35 +548,35 @@ class Var(Generic[VAR_TYPE]):
331
548
  return f"{constants.REFLEX_VAR_OPENING_TAG}{hashed_var}{constants.REFLEX_VAR_CLOSING_TAG}{self._js_expr}"
332
549
 
333
550
  @overload
334
- def to(self, output: Type[StringVar]) -> ToStringOperation: ...
551
+ def to(self, output: Type[StringVar]) -> StringVar: ...
335
552
 
336
553
  @overload
337
- def to(self, output: Type[str]) -> ToStringOperation: ...
554
+ def to(self, output: Type[str]) -> StringVar: ...
338
555
 
339
556
  @overload
340
- def to(self, output: Type[BooleanVar]) -> ToBooleanVarOperation: ...
557
+ def to(self, output: Type[BooleanVar]) -> BooleanVar: ...
341
558
 
342
559
  @overload
343
560
  def to(
344
561
  self, output: Type[NumberVar], var_type: type[int] | type[float] = float
345
- ) -> ToNumberVarOperation: ...
562
+ ) -> NumberVar: ...
346
563
 
347
564
  @overload
348
565
  def to(
349
566
  self,
350
567
  output: Type[ArrayVar],
351
568
  var_type: type[list] | type[tuple] | type[set] = list,
352
- ) -> ToArrayOperation: ...
569
+ ) -> ArrayVar: ...
353
570
 
354
571
  @overload
355
572
  def to(
356
573
  self, output: Type[ObjectVar], var_type: types.GenericType = dict
357
- ) -> ToObjectOperation: ...
574
+ ) -> ObjectVar: ...
358
575
 
359
576
  @overload
360
577
  def to(
361
578
  self, output: Type[FunctionVar], var_type: Type[Callable] = Callable
362
- ) -> ToFunctionOperation: ...
579
+ ) -> FunctionVar: ...
363
580
 
364
581
  @overload
365
582
  def to(
@@ -379,56 +596,26 @@ class Var(Generic[VAR_TYPE]):
379
596
  output: The output type.
380
597
  var_type: The type of the var.
381
598
 
382
- Raises:
383
- TypeError: If the var_type is not a supported type for the output.
384
-
385
599
  Returns:
386
600
  The converted var.
387
601
  """
388
- from reflex.event import (
389
- EventChain,
390
- EventChainVar,
391
- EventSpec,
392
- EventVar,
393
- ToEventChainVarOperation,
394
- ToEventVarOperation,
395
- )
396
-
397
- from .function import FunctionVar, ToFunctionOperation
398
- from .number import (
399
- BooleanVar,
400
- NumberVar,
401
- ToBooleanVarOperation,
402
- ToNumberVarOperation,
403
- )
404
- from .object import ObjectVar, ToObjectOperation
405
- from .sequence import ArrayVar, StringVar, ToArrayOperation, ToStringOperation
602
+ from .object import ObjectVar
406
603
 
407
604
  base_type = var_type
408
605
  if types.is_optional(base_type):
409
606
  base_type = types.get_args(base_type)[0]
410
607
 
411
- fixed_type = get_origin(base_type) or base_type
412
-
413
608
  fixed_output_type = get_origin(output) or output
414
609
 
415
610
  # If the first argument is a python type, we map it to the corresponding Var type.
416
- if fixed_output_type is dict:
417
- return self.to(ObjectVar, output)
418
- if fixed_output_type in (list, tuple, set):
419
- return self.to(ArrayVar, output)
420
- if fixed_output_type in (int, float):
421
- return self.to(NumberVar, output)
422
- if fixed_output_type is str:
423
- return self.to(StringVar, output)
424
- if fixed_output_type is bool:
425
- return self.to(BooleanVar, output)
611
+ for var_subclass in _var_subclasses[::-1]:
612
+ if fixed_output_type in var_subclass.python_types:
613
+ return self.to(var_subclass.var_subclass, output)
614
+
426
615
  if fixed_output_type is None:
427
- return ToNoneOperation.create(self)
428
- if fixed_output_type is EventSpec:
429
- return self.to(EventVar, output)
430
- if fixed_output_type is EventChain:
431
- return self.to(EventChainVar, output)
616
+ return get_to_operation(NoneVar).create(self) # type: ignore
617
+
618
+ # Handle fixed_output_type being Base or a dataclass.
432
619
  try:
433
620
  if issubclass(fixed_output_type, Base):
434
621
  return self.to(ObjectVar, output)
@@ -440,57 +627,12 @@ class Var(Generic[VAR_TYPE]):
440
627
  return self.to(ObjectVar, output)
441
628
 
442
629
  if inspect.isclass(output):
443
- if issubclass(output, BooleanVar):
444
- return ToBooleanVarOperation.create(self)
445
-
446
- if issubclass(output, NumberVar):
447
- if fixed_type is not None:
448
- if fixed_type in types.UnionTypes:
449
- inner_types = get_args(base_type)
450
- if not all(issubclass(t, (int, float)) for t in inner_types):
451
- raise TypeError(
452
- f"Unsupported type {var_type} for NumberVar. Must be int or float."
453
- )
454
-
455
- elif not issubclass(fixed_type, (int, float)):
456
- raise TypeError(
457
- f"Unsupported type {var_type} for NumberVar. Must be int or float."
458
- )
459
- return ToNumberVarOperation.create(self, var_type or float)
460
-
461
- if issubclass(output, ArrayVar):
462
- if fixed_type is not None and not issubclass(
463
- fixed_type, (list, tuple, set)
464
- ):
465
- raise TypeError(
466
- f"Unsupported type {var_type} for ArrayVar. Must be list, tuple, or set."
630
+ for var_subclass in _var_subclasses[::-1]:
631
+ if issubclass(output, var_subclass.var_subclass):
632
+ to_operation_return = var_subclass.to_var_subclass.create(
633
+ value=self, _var_type=var_type
467
634
  )
468
- return ToArrayOperation.create(self, var_type or list)
469
-
470
- if issubclass(output, StringVar):
471
- return ToStringOperation.create(self, var_type or str)
472
-
473
- if issubclass(output, EventVar):
474
- return ToEventVarOperation.create(self, var_type or EventSpec)
475
-
476
- if issubclass(output, EventChainVar):
477
- return ToEventChainVarOperation.create(self, var_type or EventChain)
478
-
479
- if issubclass(output, (ObjectVar, Base)):
480
- return ToObjectOperation.create(self, var_type or dict)
481
-
482
- if issubclass(output, FunctionVar):
483
- # if fixed_type is not None and not issubclass(fixed_type, Callable):
484
- # raise TypeError(
485
- # f"Unsupported type {var_type} for FunctionVar. Must be Callable."
486
- # )
487
- return ToFunctionOperation.create(self, var_type or Callable)
488
-
489
- if issubclass(output, NoneVar):
490
- return ToNoneOperation.create(self)
491
-
492
- if dataclasses.is_dataclass(output):
493
- return ToObjectOperation.create(self, var_type or dict)
635
+ return to_operation_return # type: ignore
494
636
 
495
637
  # If we can't determine the first argument, we just replace the _var_type.
496
638
  if not issubclass(output, Var) or var_type is None:
@@ -508,6 +650,18 @@ class Var(Generic[VAR_TYPE]):
508
650
 
509
651
  return self
510
652
 
653
+ @overload
654
+ def guess_type(self: Var[str]) -> StringVar: ...
655
+
656
+ @overload
657
+ def guess_type(self: Var[bool]) -> BooleanVar: ...
658
+
659
+ @overload
660
+ def guess_type(self: Var[int] | Var[float] | Var[int | float]) -> NumberVar: ...
661
+
662
+ @overload
663
+ def guess_type(self) -> Self: ...
664
+
511
665
  def guess_type(self) -> Var:
512
666
  """Guesses the type of the variable based on its `_var_type` attribute.
513
667
 
@@ -517,11 +671,8 @@ class Var(Generic[VAR_TYPE]):
517
671
  Raises:
518
672
  TypeError: If the type is not supported for guessing.
519
673
  """
520
- from reflex.event import EventChain, EventChainVar, EventSpec, EventVar
521
-
522
- from .number import BooleanVar, NumberVar
674
+ from .number import NumberVar
523
675
  from .object import ObjectVar
524
- from .sequence import ArrayVar, StringVar
525
676
 
526
677
  var_type = self._var_type
527
678
  if var_type is None:
@@ -558,20 +709,13 @@ class Var(Generic[VAR_TYPE]):
558
709
  if not inspect.isclass(fixed_type):
559
710
  raise TypeError(f"Unsupported type {var_type} for guess_type.")
560
711
 
561
- if issubclass(fixed_type, bool):
562
- return self.to(BooleanVar, self._var_type)
563
- if issubclass(fixed_type, (int, float)):
564
- return self.to(NumberVar, self._var_type)
565
- if issubclass(fixed_type, dict):
566
- return self.to(ObjectVar, self._var_type)
567
- if issubclass(fixed_type, (list, tuple, set)):
568
- return self.to(ArrayVar, self._var_type)
569
- if issubclass(fixed_type, str):
570
- return self.to(StringVar, self._var_type)
571
- if issubclass(fixed_type, EventSpec):
572
- return self.to(EventVar, self._var_type)
573
- if issubclass(fixed_type, EventChain):
574
- return self.to(EventChainVar, self._var_type)
712
+ if fixed_type is None:
713
+ return self.to(None)
714
+
715
+ for var_subclass in _var_subclasses[::-1]:
716
+ if issubclass(fixed_type, var_subclass.python_types):
717
+ return self.to(var_subclass.var_subclass, self._var_type)
718
+
575
719
  try:
576
720
  if issubclass(fixed_type, Base):
577
721
  return self.to(ObjectVar, self._var_type)
@@ -782,16 +926,23 @@ class Var(Generic[VAR_TYPE]):
782
926
  """
783
927
  return ~self.bool()
784
928
 
785
- def to_string(self):
929
+ def to_string(self, use_json: bool = True) -> StringVar:
786
930
  """Convert the var to a string.
787
931
 
932
+ Args:
933
+ use_json: Whether to use JSON stringify. If False, uses Object.prototype.toString.
934
+
788
935
  Returns:
789
936
  The string var.
790
937
  """
791
- from .function import JSON_STRINGIFY
938
+ from .function import JSON_STRINGIFY, PROTOTYPE_TO_STRING
792
939
  from .sequence import StringVar
793
940
 
794
- return JSON_STRINGIFY.call(self).to(StringVar)
941
+ return (
942
+ JSON_STRINGIFY.call(self).to(StringVar)
943
+ if use_json
944
+ else PROTOTYPE_TO_STRING.call(self).to(StringVar)
945
+ )
795
946
 
796
947
  def as_ref(self) -> Var:
797
948
  """Get a reference to the var.
@@ -805,7 +956,7 @@ class Var(Generic[VAR_TYPE]):
805
956
  _js_expr="refs",
806
957
  _var_data=VarData(
807
958
  imports={
808
- f"/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
959
+ f"$/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
809
960
  }
810
961
  ),
811
962
  ).to(ObjectVar, Dict[str, str])
@@ -1017,9 +1168,129 @@ class Var(Generic[VAR_TYPE]):
1017
1168
  OUTPUT = TypeVar("OUTPUT", bound=Var)
1018
1169
 
1019
1170
 
1171
+ class ToOperation:
1172
+ """A var operation that converts a var to another type."""
1173
+
1174
+ def __getattr__(self, name: str) -> Any:
1175
+ """Get an attribute of the var.
1176
+
1177
+ Args:
1178
+ name: The name of the attribute.
1179
+
1180
+ Returns:
1181
+ The attribute of the var.
1182
+ """
1183
+ from .object import ObjectVar
1184
+
1185
+ if isinstance(self, ObjectVar) and name != "_js_expr":
1186
+ return ObjectVar.__getattr__(self, name)
1187
+ return getattr(self._original, name)
1188
+
1189
+ def __post_init__(self):
1190
+ """Post initialization."""
1191
+ object.__delattr__(self, "_js_expr")
1192
+
1193
+ def __hash__(self) -> int:
1194
+ """Calculate the hash value of the object.
1195
+
1196
+ Returns:
1197
+ int: The hash value of the object.
1198
+ """
1199
+ return hash(self._original)
1200
+
1201
+ def _get_all_var_data(self) -> VarData | None:
1202
+ """Get all the var data.
1203
+
1204
+ Returns:
1205
+ The var data.
1206
+ """
1207
+ return VarData.merge(
1208
+ self._original._get_all_var_data(),
1209
+ self._var_data, # type: ignore
1210
+ )
1211
+
1212
+ @classmethod
1213
+ def create(
1214
+ cls,
1215
+ value: Var,
1216
+ _var_type: GenericType | None = None,
1217
+ _var_data: VarData | None = None,
1218
+ ):
1219
+ """Create a ToOperation.
1220
+
1221
+ Args:
1222
+ value: The value of the var.
1223
+ _var_type: The type of the Var.
1224
+ _var_data: Additional hooks and imports associated with the Var.
1225
+
1226
+ Returns:
1227
+ The ToOperation.
1228
+ """
1229
+ return cls(
1230
+ _js_expr="", # type: ignore
1231
+ _var_data=_var_data, # type: ignore
1232
+ _var_type=_var_type or cls._default_var_type, # type: ignore
1233
+ _original=value, # type: ignore
1234
+ )
1235
+
1236
+
1020
1237
  class LiteralVar(Var):
1021
1238
  """Base class for immutable literal vars."""
1022
1239
 
1240
+ def __init_subclass__(cls, **kwargs):
1241
+ """Initialize the subclass.
1242
+
1243
+ Args:
1244
+ **kwargs: Additional keyword arguments.
1245
+
1246
+ Raises:
1247
+ TypeError: If the LiteralVar subclass does not have a corresponding Var subclass.
1248
+ """
1249
+ super().__init_subclass__(**kwargs)
1250
+
1251
+ bases = cls.__bases__
1252
+
1253
+ bases_normalized = [
1254
+ base if inspect.isclass(base) else get_origin(base) for base in bases
1255
+ ]
1256
+
1257
+ possible_bases = [
1258
+ base
1259
+ for base in bases_normalized
1260
+ if issubclass(base, Var) and base != LiteralVar
1261
+ ]
1262
+
1263
+ if not possible_bases:
1264
+ raise TypeError(
1265
+ f"LiteralVar subclass {cls} must have a base class that is a subclass of Var and not LiteralVar."
1266
+ )
1267
+
1268
+ var_subclasses = [
1269
+ var_subclass
1270
+ for var_subclass in _var_subclasses
1271
+ if var_subclass.var_subclass in possible_bases
1272
+ ]
1273
+
1274
+ if not var_subclasses:
1275
+ raise TypeError(
1276
+ f"LiteralVar {cls} must have a base class annotated with `python_types`."
1277
+ )
1278
+
1279
+ if len(var_subclasses) != 1:
1280
+ raise TypeError(
1281
+ f"LiteralVar {cls} must have exactly one base class annotated with `python_types`."
1282
+ )
1283
+
1284
+ var_subclass = var_subclasses[0]
1285
+
1286
+ # Remove the old subclass, happens because __init_subclass__ is called twice
1287
+ # for each subclass. This is because of __slots__ in dataclasses.
1288
+ for var_literal_subclass in list(_var_literal_subclasses):
1289
+ if var_literal_subclass[1] is var_subclass:
1290
+ _var_literal_subclasses.remove(var_literal_subclass)
1291
+
1292
+ _var_literal_subclasses.append((cls, var_subclass))
1293
+
1023
1294
  @classmethod
1024
1295
  def create(
1025
1296
  cls,
@@ -1038,50 +1309,21 @@ class LiteralVar(Var):
1038
1309
  Raises:
1039
1310
  TypeError: If the value is not a supported type for LiteralVar.
1040
1311
  """
1041
- from .number import LiteralBooleanVar, LiteralNumberVar
1042
1312
  from .object import LiteralObjectVar
1043
- from .sequence import LiteralArrayVar, LiteralStringVar
1313
+ from .sequence import LiteralStringVar
1044
1314
 
1045
1315
  if isinstance(value, Var):
1046
1316
  if _var_data is None:
1047
1317
  return value
1048
1318
  return value._replace(merge_var_data=_var_data)
1049
1319
 
1050
- if isinstance(value, str):
1051
- return LiteralStringVar.create(value, _var_data=_var_data)
1052
-
1053
- if isinstance(value, bool):
1054
- return LiteralBooleanVar.create(value, _var_data=_var_data)
1055
-
1056
- if isinstance(value, (int, float)):
1057
- return LiteralNumberVar.create(value, _var_data=_var_data)
1058
-
1059
- if isinstance(value, dict):
1060
- return LiteralObjectVar.create(value, _var_data=_var_data)
1320
+ for literal_subclass, var_subclass in _var_literal_subclasses[::-1]:
1321
+ if isinstance(value, var_subclass.python_types):
1322
+ return literal_subclass.create(value, _var_data=_var_data)
1061
1323
 
1062
- if isinstance(value, (list, tuple, set)):
1063
- return LiteralArrayVar.create(value, _var_data=_var_data)
1064
-
1065
- if value is None:
1066
- return LiteralNoneVar.create(_var_data=_var_data)
1067
-
1068
- from reflex.event import (
1069
- EventChain,
1070
- EventHandler,
1071
- EventSpec,
1072
- LiteralEventChainVar,
1073
- LiteralEventVar,
1074
- )
1324
+ from reflex.event import EventHandler
1075
1325
  from reflex.utils.format import get_event_handler_parts
1076
1326
 
1077
- from .object import LiteralObjectVar
1078
-
1079
- if isinstance(value, EventSpec):
1080
- return LiteralEventVar.create(value, _var_data=_var_data)
1081
-
1082
- if isinstance(value, EventChain):
1083
- return LiteralEventChainVar.create(value, _var_data=_var_data)
1084
-
1085
1327
  if isinstance(value, EventHandler):
1086
1328
  return Var(_js_expr=".".join(filter(None, get_event_handler_parts(value))))
1087
1329
 
@@ -1155,6 +1397,22 @@ def serialize_literal(value: LiteralVar):
1155
1397
  return value._var_value
1156
1398
 
1157
1399
 
1400
+ def get_python_literal(value: Union[LiteralVar, Any]) -> Any | None:
1401
+ """Get the Python literal value.
1402
+
1403
+ Args:
1404
+ value: The value to get the Python literal value of.
1405
+
1406
+ Returns:
1407
+ The Python literal value.
1408
+ """
1409
+ if isinstance(value, LiteralVar):
1410
+ return value._var_value
1411
+ if isinstance(value, Var):
1412
+ return None
1413
+ return value
1414
+
1415
+
1158
1416
  P = ParamSpec("P")
1159
1417
  T = TypeVar("T")
1160
1418
 
@@ -1205,6 +1463,12 @@ def var_operation(
1205
1463
  ) -> Callable[P, ObjectVar[OBJECT_TYPE]]: ...
1206
1464
 
1207
1465
 
1466
+ @overload
1467
+ def var_operation(
1468
+ func: Callable[P, CustomVarOperationReturn[T]],
1469
+ ) -> Callable[P, Var[T]]: ...
1470
+
1471
+
1208
1472
  def var_operation(
1209
1473
  func: Callable[P, CustomVarOperationReturn[T]],
1210
1474
  ) -> Callable[P, Var[T]]:
@@ -1237,6 +1501,7 @@ def var_operation(
1237
1501
  }
1238
1502
 
1239
1503
  return CustomVarOperation.create(
1504
+ name=func.__name__,
1240
1505
  args=tuple(list(args_vars.items()) + list(kwargs_vars.items())),
1241
1506
  return_var=func(*args_vars.values(), **kwargs_vars), # type: ignore
1242
1507
  ).guess_type()
@@ -1557,8 +1822,8 @@ class ComputedVar(Var[RETURN_TYPE]):
1557
1822
  "return", Any
1558
1823
  )
1559
1824
 
1560
- kwargs["_js_expr"] = kwargs.pop("_js_expr", fget.__name__)
1561
- kwargs["_var_type"] = kwargs.pop("_var_type", hint)
1825
+ kwargs.setdefault("_js_expr", fget.__name__)
1826
+ kwargs.setdefault("_var_type", hint)
1562
1827
 
1563
1828
  Var.__init__(
1564
1829
  self,
@@ -1567,6 +1832,9 @@ class ComputedVar(Var[RETURN_TYPE]):
1567
1832
  _var_data=kwargs.pop("_var_data", None),
1568
1833
  )
1569
1834
 
1835
+ if kwargs:
1836
+ raise TypeError(f"Unexpected keyword arguments: {tuple(kwargs)}")
1837
+
1570
1838
  if backend is None:
1571
1839
  backend = fget.__name__.startswith("_")
1572
1840
 
@@ -2059,6 +2327,8 @@ def var_operation_return(
2059
2327
  class CustomVarOperation(CachedVarOperation, Var[T]):
2060
2328
  """Base class for custom var operations."""
2061
2329
 
2330
+ _name: str = dataclasses.field(default="")
2331
+
2062
2332
  _args: Tuple[Tuple[str, Var], ...] = dataclasses.field(default_factory=tuple)
2063
2333
 
2064
2334
  _return: CustomVarOperationReturn[T] = dataclasses.field(
@@ -2093,6 +2363,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
2093
2363
  @classmethod
2094
2364
  def create(
2095
2365
  cls,
2366
+ name: str,
2096
2367
  args: Tuple[Tuple[str, Var], ...],
2097
2368
  return_var: CustomVarOperationReturn[T],
2098
2369
  _var_data: VarData | None = None,
@@ -2100,6 +2371,7 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
2100
2371
  """Create a CustomVarOperation.
2101
2372
 
2102
2373
  Args:
2374
+ name: The name of the operation.
2103
2375
  args: The arguments to the operation.
2104
2376
  return_var: The return var.
2105
2377
  _var_data: Additional hooks and imports associated with the Var.
@@ -2111,12 +2383,13 @@ class CustomVarOperation(CachedVarOperation, Var[T]):
2111
2383
  _js_expr="",
2112
2384
  _var_type=return_var._var_type,
2113
2385
  _var_data=_var_data,
2386
+ _name=name,
2114
2387
  _args=args,
2115
2388
  _return=return_var,
2116
2389
  )
2117
2390
 
2118
2391
 
2119
- class NoneVar(Var[None]):
2392
+ class NoneVar(Var[None], python_types=type(None)):
2120
2393
  """A var representing None."""
2121
2394
 
2122
2395
 
@@ -2141,11 +2414,13 @@ class LiteralNoneVar(LiteralVar, NoneVar):
2141
2414
  @classmethod
2142
2415
  def create(
2143
2416
  cls,
2417
+ value: None = None,
2144
2418
  _var_data: VarData | None = None,
2145
2419
  ) -> LiteralNoneVar:
2146
2420
  """Create a var from a value.
2147
2421
 
2148
2422
  Args:
2423
+ value: The value of the var. Must be None. Existed for compatibility with LiteralVar.
2149
2424
  _var_data: Additional hooks and imports associated with the Var.
2150
2425
 
2151
2426
  Returns:
@@ -2158,48 +2433,26 @@ class LiteralNoneVar(LiteralVar, NoneVar):
2158
2433
  )
2159
2434
 
2160
2435
 
2161
- @dataclasses.dataclass(
2162
- eq=False,
2163
- frozen=True,
2164
- **{"slots": True} if sys.version_info >= (3, 10) else {},
2165
- )
2166
- class ToNoneOperation(CachedVarOperation, NoneVar):
2167
- """A var operation that converts a var to None."""
2168
-
2169
- _original_var: Var = dataclasses.field(
2170
- default_factory=lambda: LiteralNoneVar.create()
2171
- )
2172
-
2173
- @cached_property_no_lock
2174
- def _cached_var_name(self) -> str:
2175
- """Get the cached var name.
2176
-
2177
- Returns:
2178
- The cached var name.
2179
- """
2180
- return str(self._original_var)
2436
+ def get_to_operation(var_subclass: Type[Var]) -> Type[ToOperation]:
2437
+ """Get the ToOperation class for a given Var subclass.
2181
2438
 
2182
- @classmethod
2183
- def create(
2184
- cls,
2185
- var: Var,
2186
- _var_data: VarData | None = None,
2187
- ) -> ToNoneOperation:
2188
- """Create a ToNoneOperation.
2439
+ Args:
2440
+ var_subclass: The Var subclass.
2189
2441
 
2190
- Args:
2191
- var: The var to convert to None.
2192
- _var_data: Additional hooks and imports associated with the Var.
2442
+ Returns:
2443
+ The ToOperation class.
2193
2444
 
2194
- Returns:
2195
- The ToNoneOperation.
2196
- """
2197
- return ToNoneOperation(
2198
- _js_expr="",
2199
- _var_type=None,
2200
- _var_data=_var_data,
2201
- _original_var=var,
2202
- )
2445
+ Raises:
2446
+ ValueError: If the ToOperation class cannot be found.
2447
+ """
2448
+ possible_classes = [
2449
+ saved_var_subclass.to_var_subclass
2450
+ for saved_var_subclass in _var_subclasses
2451
+ if saved_var_subclass.var_subclass is var_subclass
2452
+ ]
2453
+ if not possible_classes:
2454
+ raise ValueError(f"Could not find ToOperation for {var_subclass}.")
2455
+ return possible_classes[0]
2203
2456
 
2204
2457
 
2205
2458
  @dataclasses.dataclass(
@@ -2262,68 +2515,6 @@ class StateOperation(CachedVarOperation, Var):
2262
2515
  )
2263
2516
 
2264
2517
 
2265
- class ToOperation:
2266
- """A var operation that converts a var to another type."""
2267
-
2268
- def __getattr__(self, name: str) -> Any:
2269
- """Get an attribute of the var.
2270
-
2271
- Args:
2272
- name: The name of the attribute.
2273
-
2274
- Returns:
2275
- The attribute of the var.
2276
- """
2277
- return getattr(object.__getattribute__(self, "_original"), name)
2278
-
2279
- def __post_init__(self):
2280
- """Post initialization."""
2281
- object.__delattr__(self, "_js_expr")
2282
-
2283
- def __hash__(self) -> int:
2284
- """Calculate the hash value of the object.
2285
-
2286
- Returns:
2287
- int: The hash value of the object.
2288
- """
2289
- return hash(object.__getattribute__(self, "_original"))
2290
-
2291
- def _get_all_var_data(self) -> VarData | None:
2292
- """Get all the var data.
2293
-
2294
- Returns:
2295
- The var data.
2296
- """
2297
- return VarData.merge(
2298
- object.__getattribute__(self, "_original")._get_all_var_data(),
2299
- self._var_data, # type: ignore
2300
- )
2301
-
2302
- @classmethod
2303
- def create(
2304
- cls,
2305
- value: Var,
2306
- _var_type: GenericType | None = None,
2307
- _var_data: VarData | None = None,
2308
- ):
2309
- """Create a ToOperation.
2310
-
2311
- Args:
2312
- value: The value of the var.
2313
- _var_type: The type of the Var.
2314
- _var_data: Additional hooks and imports associated with the Var.
2315
-
2316
- Returns:
2317
- The ToOperation.
2318
- """
2319
- return cls(
2320
- _js_expr="", # type: ignore
2321
- _var_data=_var_data, # type: ignore
2322
- _var_type=_var_type or cls._default_var_type, # type: ignore
2323
- _original=value, # type: ignore
2324
- )
2325
-
2326
-
2327
2518
  def get_uuid_string_var() -> Var:
2328
2519
  """Return a Var that generates a single memoized UUID via .web/utils/state.js.
2329
2520
 
@@ -2339,7 +2530,7 @@ def get_uuid_string_var() -> Var:
2339
2530
  unique_uuid_var = get_unique_variable_name()
2340
2531
  unique_uuid_var_data = VarData(
2341
2532
  imports={
2342
- f"/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore
2533
+ f"$/{constants.Dirs.STATE_PATH}": {ImportVar(tag="generateUUID")}, # type: ignore
2343
2534
  "react": "useMemo",
2344
2535
  },
2345
2536
  hooks={f"const {unique_uuid_var} = useMemo(generateUUID, [])": None},
@@ -2369,168 +2560,6 @@ def get_unique_variable_name() -> str:
2369
2560
  return get_unique_variable_name()
2370
2561
 
2371
2562
 
2372
- @dataclasses.dataclass(
2373
- eq=True,
2374
- frozen=True,
2375
- )
2376
- class VarData:
2377
- """Metadata associated with a x."""
2378
-
2379
- # The name of the enclosing state.
2380
- state: str = dataclasses.field(default="")
2381
-
2382
- # The name of the field in the state.
2383
- field_name: str = dataclasses.field(default="")
2384
-
2385
- # Imports needed to render this var
2386
- imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
2387
-
2388
- # Hooks that need to be present in the component to render this var
2389
- hooks: Tuple[str, ...] = dataclasses.field(default_factory=tuple)
2390
-
2391
- def __init__(
2392
- self,
2393
- state: str = "",
2394
- field_name: str = "",
2395
- imports: ImportDict | ParsedImportDict | None = None,
2396
- hooks: dict[str, None] | None = None,
2397
- ):
2398
- """Initialize the var data.
2399
-
2400
- Args:
2401
- state: The name of the enclosing state.
2402
- field_name: The name of the field in the state.
2403
- imports: Imports needed to render this var.
2404
- hooks: Hooks that need to be present in the component to render this var.
2405
- """
2406
- immutable_imports: ImmutableParsedImportDict = tuple(
2407
- sorted(
2408
- ((k, tuple(sorted(v))) for k, v in parse_imports(imports or {}).items())
2409
- )
2410
- )
2411
- object.__setattr__(self, "state", state)
2412
- object.__setattr__(self, "field_name", field_name)
2413
- object.__setattr__(self, "imports", immutable_imports)
2414
- object.__setattr__(self, "hooks", tuple(hooks or {}))
2415
-
2416
- def old_school_imports(self) -> ImportDict:
2417
- """Return the imports as a mutable dict.
2418
-
2419
- Returns:
2420
- The imports as a mutable dict.
2421
- """
2422
- return dict((k, list(v)) for k, v in self.imports)
2423
-
2424
- @classmethod
2425
- def merge(cls, *others: VarData | None) -> VarData | None:
2426
- """Merge multiple var data objects.
2427
-
2428
- Args:
2429
- *others: The var data objects to merge.
2430
-
2431
- Returns:
2432
- The merged var data object.
2433
- """
2434
- state = ""
2435
- field_name = ""
2436
- _imports = {}
2437
- hooks = {}
2438
- for var_data in others:
2439
- if var_data is None:
2440
- continue
2441
- state = state or var_data.state
2442
- field_name = field_name or var_data.field_name
2443
- _imports = imports.merge_imports(_imports, var_data.imports)
2444
- hooks.update(
2445
- var_data.hooks
2446
- if isinstance(var_data.hooks, dict)
2447
- else {k: None for k in var_data.hooks}
2448
- )
2449
-
2450
- if state or _imports or hooks or field_name:
2451
- return VarData(
2452
- state=state,
2453
- field_name=field_name,
2454
- imports=_imports,
2455
- hooks=hooks,
2456
- )
2457
- return None
2458
-
2459
- def __bool__(self) -> bool:
2460
- """Check if the var data is non-empty.
2461
-
2462
- Returns:
2463
- True if any field is set to a non-default value.
2464
- """
2465
- return bool(self.state or self.imports or self.hooks or self.field_name)
2466
-
2467
- @classmethod
2468
- def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData:
2469
- """Set the state of the var.
2470
-
2471
- Args:
2472
- state: The state to set or the full name of the state.
2473
- field_name: The name of the field in the state. Optional.
2474
-
2475
- Returns:
2476
- The var with the set state.
2477
- """
2478
- from reflex.utils import format
2479
-
2480
- state_name = state if isinstance(state, str) else state.get_full_name()
2481
- return VarData(
2482
- state=state_name,
2483
- field_name=field_name,
2484
- hooks={
2485
- "const {0} = useContext(StateContexts.{0})".format(
2486
- format.format_state_name(state_name)
2487
- ): None
2488
- },
2489
- imports={
2490
- f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="StateContexts")],
2491
- "react": [ImportVar(tag="useContext")],
2492
- },
2493
- )
2494
-
2495
-
2496
- def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
2497
- """Decode the state name from a formatted var.
2498
-
2499
- Args:
2500
- value: The value to extract the state name from.
2501
-
2502
- Returns:
2503
- The extracted state name and the value without the state name.
2504
- """
2505
- var_datas = []
2506
- if isinstance(value, str):
2507
- # fast path if there is no encoded VarData
2508
- if constants.REFLEX_VAR_OPENING_TAG not in value:
2509
- return None, value
2510
-
2511
- offset = 0
2512
-
2513
- # Find all tags.
2514
- while m := _decode_var_pattern.search(value):
2515
- start, end = m.span()
2516
- value = value[:start] + value[end:]
2517
-
2518
- serialized_data = m.group(1)
2519
-
2520
- if serialized_data.isnumeric() or (
2521
- serialized_data[0] == "-" and serialized_data[1:].isnumeric()
2522
- ):
2523
- # This is a global immutable var.
2524
- var = _global_vars[int(serialized_data)]
2525
- var_data = var._get_all_var_data()
2526
-
2527
- if var_data is not None:
2528
- var_datas.append(var_data)
2529
- offset += end - start
2530
-
2531
- return VarData.merge(*var_datas) if var_datas else None, value
2532
-
2533
-
2534
2563
  # Compile regex for finding reflex var tags.
2535
2564
  _decode_var_pattern_re = (
2536
2565
  rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}"