reflex 0.6.4a3__py3-none-any.whl → 0.6.5a1__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.
Files changed (228) hide show
  1. reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +0 -14
  2. reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -8
  3. reflex/.templates/web/components/shiki/code.js +16 -11
  4. reflex/.templates/web/utils/state.js +29 -21
  5. reflex/__init__.py +4 -0
  6. reflex/__init__.pyi +4 -0
  7. reflex/app.py +148 -154
  8. reflex/app_mixins/lifespan.py +5 -1
  9. reflex/app_mixins/middleware.py +3 -1
  10. reflex/app_mixins/mixin.py +3 -2
  11. reflex/base.py +2 -4
  12. reflex/compiler/compiler.py +111 -37
  13. reflex/components/base/app_wrap.pyi +17 -17
  14. reflex/components/base/bare.py +72 -3
  15. reflex/components/base/body.pyi +17 -17
  16. reflex/components/base/document.pyi +81 -81
  17. reflex/components/base/error_boundary.pyi +25 -18
  18. reflex/components/base/fragment.pyi +17 -17
  19. reflex/components/base/head.pyi +33 -33
  20. reflex/components/base/link.pyi +33 -33
  21. reflex/components/base/meta.pyi +65 -65
  22. reflex/components/base/script.py +4 -4
  23. reflex/components/base/script.pyi +23 -20
  24. reflex/components/component.py +250 -31
  25. reflex/components/core/banner.py +1 -1
  26. reflex/components/core/banner.pyi +81 -81
  27. reflex/components/core/client_side_routing.pyi +33 -33
  28. reflex/components/core/clipboard.py +2 -2
  29. reflex/components/core/clipboard.pyi +24 -18
  30. reflex/components/core/debounce.py +2 -2
  31. reflex/components/core/debounce.pyi +18 -18
  32. reflex/components/core/html.pyi +17 -17
  33. reflex/components/core/upload.py +82 -28
  34. reflex/components/core/upload.pyi +77 -72
  35. reflex/components/datadisplay/code.py +55 -40
  36. reflex/components/datadisplay/code.pyi +46 -44
  37. reflex/components/datadisplay/dataeditor.py +21 -20
  38. reflex/components/datadisplay/dataeditor.pyi +103 -35
  39. reflex/components/datadisplay/shiki_code_block.py +60 -27
  40. reflex/components/datadisplay/shiki_code_block.pyi +86 -65
  41. reflex/components/dynamic.py +9 -5
  42. reflex/components/el/element.pyi +17 -17
  43. reflex/components/el/elements/base.pyi +17 -17
  44. reflex/components/el/elements/forms.py +12 -3
  45. reflex/components/el/elements/forms.pyi +293 -233
  46. reflex/components/el/elements/inline.pyi +449 -449
  47. reflex/components/el/elements/media.pyi +401 -401
  48. reflex/components/el/elements/metadata.pyi +97 -97
  49. reflex/components/el/elements/other.pyi +113 -113
  50. reflex/components/el/elements/scripts.pyi +49 -49
  51. reflex/components/el/elements/sectioning.pyi +241 -241
  52. reflex/components/el/elements/tables.pyi +161 -161
  53. reflex/components/el/elements/typography.pyi +241 -241
  54. reflex/components/gridjs/datatable.pyi +33 -33
  55. reflex/components/lucide/icon.py +1 -1
  56. reflex/components/lucide/icon.pyi +33 -33
  57. reflex/components/markdown/markdown.py +180 -49
  58. reflex/components/markdown/markdown.pyi +36 -19
  59. reflex/components/moment/moment.py +17 -21
  60. reflex/components/moment/moment.pyi +26 -21
  61. reflex/components/next/base.pyi +17 -17
  62. reflex/components/next/image.py +3 -3
  63. reflex/components/next/image.pyi +21 -19
  64. reflex/components/next/link.pyi +17 -17
  65. reflex/components/next/video.pyi +17 -17
  66. reflex/components/plotly/plotly.py +79 -78
  67. reflex/components/plotly/plotly.pyi +91 -41
  68. reflex/components/props.py +34 -0
  69. reflex/components/radix/primitives/accordion.py +15 -8
  70. reflex/components/radix/primitives/accordion.pyi +121 -118
  71. reflex/components/radix/primitives/base.pyi +33 -33
  72. reflex/components/radix/primitives/drawer.py +41 -20
  73. reflex/components/radix/primitives/drawer.pyi +279 -190
  74. reflex/components/radix/primitives/form.py +2 -2
  75. reflex/components/radix/primitives/form.pyi +200 -167
  76. reflex/components/radix/primitives/progress.pyi +81 -81
  77. reflex/components/radix/primitives/slider.pyi +89 -83
  78. reflex/components/radix/themes/base.py +27 -1
  79. reflex/components/radix/themes/base.pyi +286 -113
  80. reflex/components/radix/themes/color_mode.py +17 -9
  81. reflex/components/radix/themes/color_mode.pyi +68 -56
  82. reflex/components/radix/themes/components/alert_dialog.py +8 -5
  83. reflex/components/radix/themes/components/alert_dialog.pyi +125 -117
  84. reflex/components/radix/themes/components/aspect_ratio.pyi +17 -17
  85. reflex/components/radix/themes/components/avatar.py +1 -5
  86. reflex/components/radix/themes/components/avatar.pyi +17 -17
  87. reflex/components/radix/themes/components/badge.py +1 -5
  88. reflex/components/radix/themes/components/badge.pyi +17 -17
  89. reflex/components/radix/themes/components/button.pyi +18 -21
  90. reflex/components/radix/themes/components/callout.py +1 -4
  91. reflex/components/radix/themes/components/callout.pyi +81 -81
  92. reflex/components/radix/themes/components/card.py +1 -3
  93. reflex/components/radix/themes/components/card.pyi +17 -17
  94. reflex/components/radix/themes/components/checkbox.py +4 -8
  95. reflex/components/radix/themes/components/checkbox.pyi +61 -52
  96. reflex/components/radix/themes/components/checkbox_cards.pyi +33 -33
  97. reflex/components/radix/themes/components/checkbox_group.pyi +33 -33
  98. reflex/components/radix/themes/components/context_menu.py +121 -28
  99. reflex/components/radix/themes/components/context_menu.pyi +250 -147
  100. reflex/components/radix/themes/components/data_list.pyi +65 -65
  101. reflex/components/radix/themes/components/dialog.py +11 -11
  102. reflex/components/radix/themes/components/dialog.pyi +135 -120
  103. reflex/components/radix/themes/components/dropdown_menu.py +14 -25
  104. reflex/components/radix/themes/components/dropdown_menu.pyi +157 -145
  105. reflex/components/radix/themes/components/hover_card.py +19 -7
  106. reflex/components/radix/themes/components/hover_card.pyi +102 -67
  107. reflex/components/radix/themes/components/icon_button.pyi +18 -21
  108. reflex/components/radix/themes/components/inset.py +1 -3
  109. reflex/components/radix/themes/components/inset.pyi +17 -17
  110. reflex/components/radix/themes/components/popover.py +22 -13
  111. reflex/components/radix/themes/components/popover.pyi +98 -72
  112. reflex/components/radix/themes/components/progress.pyi +17 -17
  113. reflex/components/radix/themes/components/radio.pyi +17 -17
  114. reflex/components/radix/themes/components/radio_cards.py +2 -2
  115. reflex/components/radix/themes/components/radio_cards.pyi +37 -34
  116. reflex/components/radix/themes/components/radio_group.py +3 -7
  117. reflex/components/radix/themes/components/radio_group.pyi +69 -66
  118. reflex/components/radix/themes/components/scroll_area.py +1 -3
  119. reflex/components/radix/themes/components/scroll_area.pyi +17 -17
  120. reflex/components/radix/themes/components/segmented_control.pyi +37 -34
  121. reflex/components/radix/themes/components/select.py +7 -11
  122. reflex/components/radix/themes/components/select.pyi +175 -154
  123. reflex/components/radix/themes/components/separator.py +1 -4
  124. reflex/components/radix/themes/components/separator.pyi +17 -17
  125. reflex/components/radix/themes/components/skeleton.pyi +17 -17
  126. reflex/components/radix/themes/components/slider.py +12 -21
  127. reflex/components/radix/themes/components/slider.pyi +47 -25
  128. reflex/components/radix/themes/components/spinner.py +1 -4
  129. reflex/components/radix/themes/components/spinner.pyi +17 -17
  130. reflex/components/radix/themes/components/switch.py +3 -6
  131. reflex/components/radix/themes/components/switch.pyi +21 -18
  132. reflex/components/radix/themes/components/table.py +21 -5
  133. reflex/components/radix/themes/components/table.pyi +392 -116
  134. reflex/components/radix/themes/components/tabs.py +3 -6
  135. reflex/components/radix/themes/components/tabs.pyi +89 -83
  136. reflex/components/radix/themes/components/text_area.py +1 -5
  137. reflex/components/radix/themes/components/text_area.pyi +43 -20
  138. reflex/components/radix/themes/components/text_field.py +1 -5
  139. reflex/components/radix/themes/components/text_field.pyi +101 -55
  140. reflex/components/radix/themes/components/tooltip.py +5 -7
  141. reflex/components/radix/themes/components/tooltip.pyi +25 -22
  142. reflex/components/radix/themes/layout/base.py +2 -27
  143. reflex/components/radix/themes/layout/base.pyi +82 -82
  144. reflex/components/radix/themes/layout/box.pyi +17 -17
  145. reflex/components/radix/themes/layout/center.pyi +17 -17
  146. reflex/components/radix/themes/layout/container.pyi +17 -17
  147. reflex/components/radix/themes/layout/flex.py +1 -6
  148. reflex/components/radix/themes/layout/flex.pyi +17 -17
  149. reflex/components/radix/themes/layout/grid.py +1 -6
  150. reflex/components/radix/themes/layout/grid.pyi +17 -17
  151. reflex/components/radix/themes/layout/list.py +20 -15
  152. reflex/components/radix/themes/layout/list.pyi +175 -92
  153. reflex/components/radix/themes/layout/section.pyi +17 -17
  154. reflex/components/radix/themes/layout/spacer.pyi +17 -17
  155. reflex/components/radix/themes/layout/stack.py +6 -6
  156. reflex/components/radix/themes/layout/stack.pyi +91 -62
  157. reflex/components/radix/themes/typography/blockquote.py +2 -8
  158. reflex/components/radix/themes/typography/blockquote.pyi +17 -17
  159. reflex/components/radix/themes/typography/code.py +4 -10
  160. reflex/components/radix/themes/typography/code.pyi +19 -18
  161. reflex/components/radix/themes/typography/heading.py +4 -11
  162. reflex/components/radix/themes/typography/heading.pyi +19 -18
  163. reflex/components/radix/themes/typography/link.py +4 -10
  164. reflex/components/radix/themes/typography/link.pyi +19 -18
  165. reflex/components/radix/themes/typography/text.py +4 -11
  166. reflex/components/radix/themes/typography/text.pyi +115 -114
  167. reflex/components/react_player/audio.pyi +58 -33
  168. reflex/components/react_player/react_player.py +17 -17
  169. reflex/components/react_player/react_player.pyi +55 -33
  170. reflex/components/react_player/video.pyi +58 -33
  171. reflex/components/recharts/cartesian.py +45 -45
  172. reflex/components/recharts/cartesian.pyi +389 -304
  173. reflex/components/recharts/charts.py +22 -22
  174. reflex/components/recharts/charts.pyi +226 -179
  175. reflex/components/recharts/general.py +26 -27
  176. reflex/components/recharts/general.pyi +106 -99
  177. reflex/components/recharts/polar.py +33 -33
  178. reflex/components/recharts/polar.pyi +70 -64
  179. reflex/components/recharts/recharts.pyi +33 -33
  180. reflex/components/sonner/toast.py +9 -36
  181. reflex/components/sonner/toast.pyi +20 -24
  182. reflex/components/suneditor/editor.py +8 -8
  183. reflex/components/suneditor/editor.pyi +50 -25
  184. reflex/components/tags/iter_tag.py +1 -10
  185. reflex/components/tags/tag.py +1 -4
  186. reflex/config.py +198 -35
  187. reflex/constants/__init__.py +4 -16
  188. reflex/constants/base.py +7 -14
  189. reflex/constants/colors.py +0 -1
  190. reflex/constants/installer.py +12 -7
  191. reflex/constants/state.py +4 -0
  192. reflex/custom_components/custom_components.py +6 -6
  193. reflex/event.py +486 -241
  194. reflex/experimental/client_state.py +9 -9
  195. reflex/experimental/layout.py +2 -2
  196. reflex/experimental/layout.pyi +95 -87
  197. reflex/experimental/misc.py +1 -1
  198. reflex/istate/__init__.py +1 -0
  199. reflex/istate/proxy.py +33 -0
  200. reflex/istate/wrappers.py +27 -0
  201. reflex/model.py +7 -7
  202. reflex/page.py +2 -1
  203. reflex/reflex.py +142 -8
  204. reflex/state.py +127 -46
  205. reflex/testing.py +9 -7
  206. reflex/utils/console.py +0 -1
  207. reflex/utils/exceptions.py +31 -3
  208. reflex/utils/exec.py +33 -14
  209. reflex/utils/format.py +15 -12
  210. reflex/utils/net.py +1 -1
  211. reflex/utils/path_ops.py +2 -2
  212. reflex/utils/prerequisites.py +82 -46
  213. reflex/utils/pyi_generator.py +63 -20
  214. reflex/utils/registry.py +1 -1
  215. reflex/utils/serializers.py +75 -36
  216. reflex/utils/telemetry.py +3 -2
  217. reflex/utils/types.py +125 -10
  218. reflex/vars/base.py +131 -119
  219. reflex/vars/function.py +59 -12
  220. reflex/vars/number.py +3 -1
  221. reflex/vars/object.py +30 -24
  222. reflex/vars/sequence.py +7 -7
  223. {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/METADATA +3 -3
  224. reflex-0.6.5a1.dist-info/RECORD +394 -0
  225. reflex-0.6.4a3.dist-info/RECORD +0 -391
  226. {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/LICENSE +0 -0
  227. {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/WHEEL +0 -0
  228. {reflex-0.6.4a3.dist-info → reflex-0.6.5a1.dist-info}/entry_points.txt +0 -0
@@ -18,6 +18,7 @@ from typing import (
18
18
  Set,
19
19
  Tuple,
20
20
  Type,
21
+ TypeVar,
21
22
  Union,
22
23
  get_type_hints,
23
24
  overload,
@@ -32,17 +33,33 @@ from reflex.utils import types
32
33
  SerializedType = Union[str, bool, int, float, list, dict, None]
33
34
 
34
35
 
35
- Serializer = Callable[[Type], SerializedType]
36
+ Serializer = Callable[[Any], SerializedType]
36
37
 
37
38
 
38
39
  SERIALIZERS: dict[Type, Serializer] = {}
39
40
  SERIALIZER_TYPES: dict[Type, Type] = {}
40
41
 
42
+ SERIALIZED_FUNCTION = TypeVar("SERIALIZED_FUNCTION", bound=Serializer)
41
43
 
44
+
45
+ @overload
46
+ def serializer(
47
+ fn: None = None,
48
+ to: Type[SerializedType] | None = None,
49
+ ) -> Callable[[SERIALIZED_FUNCTION], SERIALIZED_FUNCTION]: ...
50
+
51
+
52
+ @overload
42
53
  def serializer(
43
- fn: Serializer | None = None,
44
- to: Type | None = None,
45
- ) -> Serializer:
54
+ fn: SERIALIZED_FUNCTION,
55
+ to: Type[SerializedType] | None = None,
56
+ ) -> SERIALIZED_FUNCTION: ...
57
+
58
+
59
+ def serializer(
60
+ fn: SERIALIZED_FUNCTION | None = None,
61
+ to: Any = None,
62
+ ) -> SERIALIZED_FUNCTION | Callable[[SERIALIZED_FUNCTION], SERIALIZED_FUNCTION]:
46
63
  """Decorator to add a serializer for a given type.
47
64
 
48
65
  Args:
@@ -51,43 +68,44 @@ def serializer(
51
68
 
52
69
  Returns:
53
70
  The decorated function.
54
-
55
- Raises:
56
- ValueError: If the function does not take a single argument.
57
71
  """
58
- if fn is None:
59
- # If the function is not provided, return a partial that acts as a decorator.
60
- return functools.partial(serializer, to=to) # type: ignore
61
72
 
62
- # Check the type hints to get the type of the argument.
63
- type_hints = get_type_hints(fn)
64
- args = [arg for arg in type_hints if arg != "return"]
73
+ def wrapper(fn: SERIALIZED_FUNCTION) -> SERIALIZED_FUNCTION:
74
+ # Check the type hints to get the type of the argument.
75
+ type_hints = get_type_hints(fn)
76
+ args = [arg for arg in type_hints if arg != "return"]
77
+
78
+ # Make sure the function takes a single argument.
79
+ if len(args) != 1:
80
+ raise ValueError("Serializer must take a single argument.")
81
+
82
+ # Get the type of the argument.
83
+ type_ = type_hints[args[0]]
65
84
 
66
- # Make sure the function takes a single argument.
67
- if len(args) != 1:
68
- raise ValueError("Serializer must take a single argument.")
85
+ # Make sure the type is not already registered.
86
+ registered_fn = SERIALIZERS.get(type_)
87
+ if registered_fn is not None and registered_fn != fn:
88
+ raise ValueError(
89
+ f"Serializer for type {type_} is already registered as {registered_fn.__qualname__}."
90
+ )
69
91
 
70
- # Get the type of the argument.
71
- type_ = type_hints[args[0]]
92
+ to_type = to or type_hints.get("return")
72
93
 
73
- # Make sure the type is not already registered.
74
- registered_fn = SERIALIZERS.get(type_)
75
- if registered_fn is not None and registered_fn != fn:
76
- raise ValueError(
77
- f"Serializer for type {type_} is already registered as {registered_fn.__qualname__}."
78
- )
94
+ # Apply type transformation if requested
95
+ if to_type:
96
+ SERIALIZER_TYPES[type_] = to_type
97
+ get_serializer_type.cache_clear()
79
98
 
80
- # Apply type transformation if requested
81
- if to is not None:
82
- SERIALIZER_TYPES[type_] = to
83
- get_serializer_type.cache_clear()
99
+ # Register the serializer.
100
+ SERIALIZERS[type_] = fn
101
+ get_serializer.cache_clear()
84
102
 
85
- # Register the serializer.
86
- SERIALIZERS[type_] = fn
87
- get_serializer.cache_clear()
103
+ # Return the function.
104
+ return fn
88
105
 
89
- # Return the function.
90
- return fn
106
+ if fn is not None:
107
+ return wrapper(fn)
108
+ return wrapper
91
109
 
92
110
 
93
111
  @overload
@@ -189,16 +207,37 @@ def get_serializer_type(type_: Type) -> Optional[Type]:
189
207
  return None
190
208
 
191
209
 
192
- def has_serializer(type_: Type) -> bool:
210
+ def has_serializer(type_: Type, into_type: Type | None = None) -> bool:
193
211
  """Check if there is a serializer for the type.
194
212
 
195
213
  Args:
196
214
  type_: The type to check.
215
+ into_type: The type to serialize into.
197
216
 
198
217
  Returns:
199
218
  Whether there is a serializer for the type.
200
219
  """
201
- return get_serializer(type_) is not None
220
+ serializer_for_type = get_serializer(type_)
221
+ return serializer_for_type is not None and (
222
+ into_type is None or get_serializer_type(type_) == into_type
223
+ )
224
+
225
+
226
+ def can_serialize(type_: Type, into_type: Type | None = None) -> bool:
227
+ """Check if there is a serializer for the type.
228
+
229
+ Args:
230
+ type_: The type to check.
231
+ into_type: The type to serialize into.
232
+
233
+ Returns:
234
+ Whether there is a serializer for the type.
235
+ """
236
+ return has_serializer(type_, into_type) or (
237
+ isinstance(type_, type)
238
+ and dataclasses.is_dataclass(type_)
239
+ and (into_type is None or into_type is dict)
240
+ )
202
241
 
203
242
 
204
243
  @serializer(to=str)
@@ -214,7 +253,7 @@ def serialize_type(value: type) -> str:
214
253
  return value.__name__
215
254
 
216
255
 
217
- @serializer
256
+ @serializer(to=dict)
218
257
  def serialize_base(value: Base) -> dict:
219
258
  """Serialize a Base instance.
220
259
 
reflex/utils/telemetry.py CHANGED
@@ -8,6 +8,8 @@ import multiprocessing
8
8
  import platform
9
9
  import warnings
10
10
 
11
+ from reflex.config import environment
12
+
11
13
  try:
12
14
  from datetime import UTC, datetime
13
15
  except ImportError:
@@ -20,7 +22,6 @@ import psutil
20
22
 
21
23
  from reflex import constants
22
24
  from reflex.utils import console
23
- from reflex.utils.exec import should_skip_compile
24
25
  from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash
25
26
 
26
27
  POSTHOG_API_URL: str = "https://app.posthog.com/capture/"
@@ -94,7 +95,7 @@ def _raise_on_missing_project_hash() -> bool:
94
95
  False when compilation should be skipped (i.e. no .web directory is required).
95
96
  Otherwise return True.
96
97
  """
97
- return not should_skip_compile()
98
+ return not environment.REFLEX_SKIP_COMPILE.get()
98
99
 
99
100
 
100
101
  def _prepare_event(event: str, **kwargs) -> dict:
reflex/utils/types.py CHANGED
@@ -26,9 +26,7 @@ from typing import (
26
26
  get_args,
27
27
  get_type_hints,
28
28
  )
29
- from typing import (
30
- get_origin as get_origin_og,
31
- )
29
+ from typing import get_origin as get_origin_og
32
30
 
33
31
  import sqlalchemy
34
32
 
@@ -42,12 +40,7 @@ except ModuleNotFoundError:
42
40
 
43
41
  from sqlalchemy.ext.associationproxy import AssociationProxyInstance
44
42
  from sqlalchemy.ext.hybrid import hybrid_property
45
- from sqlalchemy.orm import (
46
- DeclarativeBase,
47
- Mapped,
48
- QueryableAttribute,
49
- Relationship,
50
- )
43
+ from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
51
44
 
52
45
  from reflex import constants
53
46
  from reflex.base import Base
@@ -510,16 +503,72 @@ def _issubclass(cls: GenericType, cls_check: GenericType, instance: Any = None)
510
503
  raise TypeError(f"Invalid type for issubclass: {cls_base}") from te
511
504
 
512
505
 
513
- def _isinstance(obj: Any, cls: GenericType) -> bool:
506
+ def _isinstance(obj: Any, cls: GenericType, nested: bool = False) -> bool:
514
507
  """Check if an object is an instance of a class.
515
508
 
516
509
  Args:
517
510
  obj: The object to check.
518
511
  cls: The class to check against.
512
+ nested: Whether the check is nested.
519
513
 
520
514
  Returns:
521
515
  Whether the object is an instance of the class.
522
516
  """
517
+ if cls is Any:
518
+ return True
519
+
520
+ if cls is None or cls is type(None):
521
+ return obj is None
522
+
523
+ if is_literal(cls):
524
+ return obj in get_args(cls)
525
+
526
+ if is_union(cls):
527
+ return any(_isinstance(obj, arg) for arg in get_args(cls))
528
+
529
+ origin = get_origin(cls)
530
+
531
+ if origin is None:
532
+ # cls is a simple class
533
+ return isinstance(obj, cls)
534
+
535
+ args = get_args(cls)
536
+
537
+ if not args:
538
+ # cls is a simple generic class
539
+ return isinstance(obj, origin)
540
+
541
+ if nested and args:
542
+ if origin is list:
543
+ return isinstance(obj, list) and all(
544
+ _isinstance(item, args[0]) for item in obj
545
+ )
546
+ if origin is tuple:
547
+ if args[-1] is Ellipsis:
548
+ return isinstance(obj, tuple) and all(
549
+ _isinstance(item, args[0]) for item in obj
550
+ )
551
+ return (
552
+ isinstance(obj, tuple)
553
+ and len(obj) == len(args)
554
+ and all(_isinstance(item, arg) for item, arg in zip(obj, args))
555
+ )
556
+ if origin is dict:
557
+ return isinstance(obj, dict) and all(
558
+ _isinstance(key, args[0]) and _isinstance(value, args[1])
559
+ for key, value in obj.items()
560
+ )
561
+ if origin is set:
562
+ return isinstance(obj, set) and all(
563
+ _isinstance(item, args[0]) for item in obj
564
+ )
565
+
566
+ if args:
567
+ from reflex.vars import Field
568
+
569
+ if origin is Field:
570
+ return _isinstance(obj, args[0])
571
+
523
572
  return isinstance(obj, get_base_class(cls))
524
573
 
525
574
 
@@ -724,3 +773,69 @@ def validate_parameter_literals(func):
724
773
  # Store this here for performance.
725
774
  StateBases = get_base_class(StateVar)
726
775
  StateIterBases = get_base_class(StateIterVar)
776
+
777
+
778
+ def typehint_issubclass(possible_subclass: Any, possible_superclass: Any) -> bool:
779
+ """Check if a type hint is a subclass of another type hint.
780
+
781
+ Args:
782
+ possible_subclass: The type hint to check.
783
+ possible_superclass: The type hint to check against.
784
+
785
+ Returns:
786
+ Whether the type hint is a subclass of the other type hint.
787
+ """
788
+ if possible_superclass is Any:
789
+ return True
790
+ if possible_subclass is Any:
791
+ return False
792
+
793
+ provided_type_origin = get_origin(possible_subclass)
794
+ accepted_type_origin = get_origin(possible_superclass)
795
+
796
+ if provided_type_origin is None and accepted_type_origin is None:
797
+ # In this case, we are dealing with a non-generic type, so we can use issubclass
798
+ return issubclass(possible_subclass, possible_superclass)
799
+
800
+ # Remove this check when Python 3.10 is the minimum supported version
801
+ if hasattr(types, "UnionType"):
802
+ provided_type_origin = (
803
+ Union if provided_type_origin is types.UnionType else provided_type_origin
804
+ )
805
+ accepted_type_origin = (
806
+ Union if accepted_type_origin is types.UnionType else accepted_type_origin
807
+ )
808
+
809
+ # Get type arguments (e.g., [float, int] for Dict[float, int])
810
+ provided_args = get_args(possible_subclass)
811
+ accepted_args = get_args(possible_superclass)
812
+
813
+ if accepted_type_origin is Union:
814
+ if provided_type_origin is not Union:
815
+ return any(
816
+ typehint_issubclass(possible_subclass, accepted_arg)
817
+ for accepted_arg in accepted_args
818
+ )
819
+ return all(
820
+ any(
821
+ typehint_issubclass(provided_arg, accepted_arg)
822
+ for accepted_arg in accepted_args
823
+ )
824
+ for provided_arg in provided_args
825
+ )
826
+
827
+ # Check if the origin of both types is the same (e.g., list for List[int])
828
+ # This probably should be issubclass instead of ==
829
+ if (provided_type_origin or possible_subclass) != (
830
+ accepted_type_origin or possible_superclass
831
+ ):
832
+ return False
833
+
834
+ # Ensure all specific types are compatible with accepted types
835
+ # Note this is not necessarily correct, as it doesn't check against contravariance and covariance
836
+ # It also ignores when the length of the arguments is different
837
+ return all(
838
+ typehint_issubclass(provided_arg, accepted_arg)
839
+ for provided_arg, accepted_arg in zip(provided_args, accepted_args)
840
+ if accepted_arg is not Any
841
+ )