reflex 0.5.9a1__py3-none-any.whl → 0.5.10__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 (169) hide show
  1. reflex/__init__.py +21 -2
  2. reflex/__init__.pyi +0 -1
  3. reflex/app.py +1 -1
  4. reflex/components/__init__.py +0 -1
  5. reflex/components/__init__.pyi +0 -1
  6. reflex/components/markdown/markdown.py +1 -1
  7. reflex/components/recharts/cartesian.py +3 -0
  8. reflex/components/recharts/cartesian.pyi +2 -0
  9. reflex/components/sonner/toast.py +27 -1
  10. reflex/components/sonner/toast.pyi +5 -0
  11. reflex/constants/base.py +2 -0
  12. reflex/constants/installer.py +2 -0
  13. reflex/experimental/hooks.py +1 -1
  14. reflex/reflex.py +0 -15
  15. reflex/state.py +22 -22
  16. reflex/testing.py +8 -3
  17. reflex/utils/net.py +43 -0
  18. reflex/utils/prerequisites.py +27 -126
  19. reflex/utils/registry.py +48 -0
  20. reflex/utils/types.py +1 -1
  21. reflex/vars.py +4 -2
  22. {reflex-0.5.9a1.dist-info → reflex-0.5.10.dist-info}/METADATA +2 -1
  23. {reflex-0.5.9a1.dist-info → reflex-0.5.10.dist-info}/RECORD +26 -167
  24. reflex/components/chakra/__init__.py +0 -204
  25. reflex/components/chakra/base.py +0 -210
  26. reflex/components/chakra/base.pyi +0 -319
  27. reflex/components/chakra/datadisplay/__init__.py +0 -10
  28. reflex/components/chakra/datadisplay/badge.py +0 -16
  29. reflex/components/chakra/datadisplay/badge.pyi +0 -96
  30. reflex/components/chakra/datadisplay/code.py +0 -11
  31. reflex/components/chakra/datadisplay/code.pyi +0 -87
  32. reflex/components/chakra/datadisplay/divider.py +0 -20
  33. reflex/components/chakra/datadisplay/divider.pyi +0 -100
  34. reflex/components/chakra/datadisplay/keyboard_key.py +0 -9
  35. reflex/components/chakra/datadisplay/keyboard_key.pyi +0 -87
  36. reflex/components/chakra/datadisplay/list.py +0 -60
  37. reflex/components/chakra/datadisplay/list.pyi +0 -339
  38. reflex/components/chakra/datadisplay/stat.py +0 -80
  39. reflex/components/chakra/datadisplay/stat.pyi +0 -477
  40. reflex/components/chakra/datadisplay/table.py +0 -305
  41. reflex/components/chakra/datadisplay/table.pyi +0 -737
  42. reflex/components/chakra/datadisplay/tag.py +0 -85
  43. reflex/components/chakra/datadisplay/tag.pyi +0 -433
  44. reflex/components/chakra/disclosure/__init__.py +0 -14
  45. reflex/components/chakra/disclosure/accordion.py +0 -110
  46. reflex/components/chakra/disclosure/accordion.pyi +0 -411
  47. reflex/components/chakra/disclosure/tabs.py +0 -117
  48. reflex/components/chakra/disclosure/tabs.pyi +0 -496
  49. reflex/components/chakra/disclosure/transition.py +0 -73
  50. reflex/components/chakra/disclosure/transition.pyi +0 -509
  51. reflex/components/chakra/disclosure/visuallyhidden.py +0 -9
  52. reflex/components/chakra/disclosure/visuallyhidden.pyi +0 -87
  53. reflex/components/chakra/feedback/__init__.py +0 -7
  54. reflex/components/chakra/feedback/alert.py +0 -68
  55. reflex/components/chakra/feedback/alert.pyi +0 -335
  56. reflex/components/chakra/feedback/circularprogress.py +0 -68
  57. reflex/components/chakra/feedback/circularprogress.pyi +0 -185
  58. reflex/components/chakra/feedback/progress.py +0 -33
  59. reflex/components/chakra/feedback/progress.pyi +0 -101
  60. reflex/components/chakra/feedback/skeleton.py +0 -70
  61. reflex/components/chakra/feedback/skeleton.pyi +0 -271
  62. reflex/components/chakra/feedback/spinner.py +0 -25
  63. reflex/components/chakra/feedback/spinner.pyi +0 -102
  64. reflex/components/chakra/forms/__init__.py +0 -49
  65. reflex/components/chakra/forms/button.py +0 -83
  66. reflex/components/chakra/forms/button.pyi +0 -262
  67. reflex/components/chakra/forms/checkbox.py +0 -77
  68. reflex/components/chakra/forms/checkbox.pyi +0 -247
  69. reflex/components/chakra/forms/colormodeswitch.py +0 -104
  70. reflex/components/chakra/forms/colormodeswitch.pyi +0 -473
  71. reflex/components/chakra/forms/date_picker.py +0 -11
  72. reflex/components/chakra/forms/date_picker.pyi +0 -129
  73. reflex/components/chakra/forms/date_time_picker.py +0 -11
  74. reflex/components/chakra/forms/date_time_picker.pyi +0 -129
  75. reflex/components/chakra/forms/editable.py +0 -67
  76. reflex/components/chakra/forms/editable.pyi +0 -343
  77. reflex/components/chakra/forms/email.py +0 -11
  78. reflex/components/chakra/forms/email.pyi +0 -129
  79. reflex/components/chakra/forms/form.py +0 -103
  80. reflex/components/chakra/forms/form.pyi +0 -484
  81. reflex/components/chakra/forms/iconbutton.py +0 -38
  82. reflex/components/chakra/forms/iconbutton.pyi +0 -108
  83. reflex/components/chakra/forms/input.py +0 -130
  84. reflex/components/chakra/forms/input.pyi +0 -566
  85. reflex/components/chakra/forms/multiselect.py +0 -355
  86. reflex/components/chakra/forms/numberinput.py +0 -128
  87. reflex/components/chakra/forms/numberinput.pyi +0 -442
  88. reflex/components/chakra/forms/password.py +0 -11
  89. reflex/components/chakra/forms/password.pyi +0 -129
  90. reflex/components/chakra/forms/pininput.py +0 -201
  91. reflex/components/chakra/forms/pininput.pyi +0 -215
  92. reflex/components/chakra/forms/radio.py +0 -101
  93. reflex/components/chakra/forms/radio.pyi +0 -197
  94. reflex/components/chakra/forms/rangeslider.py +0 -149
  95. reflex/components/chakra/forms/rangeslider.pyi +0 -354
  96. reflex/components/chakra/forms/select.py +0 -103
  97. reflex/components/chakra/forms/select.pyi +0 -204
  98. reflex/components/chakra/forms/slider.py +0 -130
  99. reflex/components/chakra/forms/slider.pyi +0 -449
  100. reflex/components/chakra/forms/switch.py +0 -49
  101. reflex/components/chakra/forms/switch.pyi +0 -159
  102. reflex/components/chakra/forms/textarea.py +0 -79
  103. reflex/components/chakra/forms/textarea.pyi +0 -123
  104. reflex/components/chakra/forms/time_picker.py +0 -11
  105. reflex/components/chakra/forms/time_picker.pyi +0 -129
  106. reflex/components/chakra/layout/__init__.py +0 -14
  107. reflex/components/chakra/layout/aspect_ratio.py +0 -13
  108. reflex/components/chakra/layout/aspect_ratio.pyi +0 -89
  109. reflex/components/chakra/layout/box.py +0 -31
  110. reflex/components/chakra/layout/box.pyi +0 -93
  111. reflex/components/chakra/layout/card.py +0 -100
  112. reflex/components/chakra/layout/card.pyi +0 -379
  113. reflex/components/chakra/layout/center.py +0 -21
  114. reflex/components/chakra/layout/center.pyi +0 -239
  115. reflex/components/chakra/layout/container.py +0 -13
  116. reflex/components/chakra/layout/container.pyi +0 -89
  117. reflex/components/chakra/layout/flex.py +0 -33
  118. reflex/components/chakra/layout/flex.pyi +0 -101
  119. reflex/components/chakra/layout/grid.py +0 -125
  120. reflex/components/chakra/layout/grid.pyi +0 -293
  121. reflex/components/chakra/layout/spacer.py +0 -9
  122. reflex/components/chakra/layout/spacer.pyi +0 -87
  123. reflex/components/chakra/layout/stack.py +0 -48
  124. reflex/components/chakra/layout/stack.pyi +0 -305
  125. reflex/components/chakra/layout/wrap.py +0 -57
  126. reflex/components/chakra/layout/wrap.pyi +0 -179
  127. reflex/components/chakra/media/__init__.py +0 -7
  128. reflex/components/chakra/media/avatar.py +0 -58
  129. reflex/components/chakra/media/avatar.pyi +0 -267
  130. reflex/components/chakra/media/icon.py +0 -111
  131. reflex/components/chakra/media/icon.pyi +0 -171
  132. reflex/components/chakra/media/image.py +0 -74
  133. reflex/components/chakra/media/image.pyi +0 -118
  134. reflex/components/chakra/navigation/__init__.py +0 -18
  135. reflex/components/chakra/navigation/breadcrumb.py +0 -98
  136. reflex/components/chakra/navigation/breadcrumb.pyi +0 -351
  137. reflex/components/chakra/navigation/link.py +0 -62
  138. reflex/components/chakra/navigation/link.pyi +0 -105
  139. reflex/components/chakra/navigation/linkoverlay.py +0 -22
  140. reflex/components/chakra/navigation/linkoverlay.pyi +0 -167
  141. reflex/components/chakra/navigation/stepper.py +0 -113
  142. reflex/components/chakra/navigation/stepper.pyi +0 -762
  143. reflex/components/chakra/overlay/__init__.py +0 -50
  144. reflex/components/chakra/overlay/alertdialog.py +0 -153
  145. reflex/components/chakra/overlay/alertdialog.pyi +0 -612
  146. reflex/components/chakra/overlay/drawer.py +0 -168
  147. reflex/components/chakra/overlay/drawer.pyi +0 -652
  148. reflex/components/chakra/overlay/menu.py +0 -225
  149. reflex/components/chakra/overlay/menu.pyi +0 -708
  150. reflex/components/chakra/overlay/modal.py +0 -166
  151. reflex/components/chakra/overlay/modal.pyi +0 -600
  152. reflex/components/chakra/overlay/popover.py +0 -183
  153. reflex/components/chakra/overlay/popover.pyi +0 -757
  154. reflex/components/chakra/overlay/tooltip.py +0 -70
  155. reflex/components/chakra/overlay/tooltip.pyi +0 -129
  156. reflex/components/chakra/typography/__init__.py +0 -10
  157. reflex/components/chakra/typography/heading.py +0 -16
  158. reflex/components/chakra/typography/heading.pyi +0 -96
  159. reflex/components/chakra/typography/highlight.py +0 -23
  160. reflex/components/chakra/typography/highlight.pyi +0 -91
  161. reflex/components/chakra/typography/span.py +0 -15
  162. reflex/components/chakra/typography/span.pyi +0 -89
  163. reflex/components/chakra/typography/text.py +0 -18
  164. reflex/components/chakra/typography/text.pyi +0 -91
  165. reflex/components/media/__init__.py +0 -1
  166. reflex/components/media/icon.py +0 -3
  167. {reflex-0.5.9a1.dist-info → reflex-0.5.10.dist-info}/LICENSE +0 -0
  168. {reflex-0.5.9a1.dist-info → reflex-0.5.10.dist-info}/WHEEL +0 -0
  169. {reflex-0.5.9a1.dist-info → reflex-0.5.10.dist-info}/entry_points.txt +0 -0
reflex/__init__.py CHANGED
@@ -270,7 +270,7 @@ _MAPPING: dict = {
270
270
  "EditorButtonList",
271
271
  "EditorOptions",
272
272
  ],
273
- "components": ["el", "chakra", "radix", "lucide", "recharts", "next"],
273
+ "components": ["el", "radix", "lucide", "recharts", "next"],
274
274
  "components.markdown": ["markdown"],
275
275
  **RADIX_MAPPING,
276
276
  "components.plotly": ["plotly"],
@@ -342,8 +342,27 @@ _SUBMODULES: set[str] = {
342
342
  "compiler",
343
343
  }
344
344
  _SUBMOD_ATTRS: dict = _MAPPING
345
- __getattr__, __dir__, __all__ = lazy_loader.attach(
345
+ getattr, __dir__, __all__ = lazy_loader.attach(
346
346
  __name__,
347
347
  submodules=_SUBMODULES,
348
348
  submod_attrs=_SUBMOD_ATTRS,
349
349
  )
350
+
351
+
352
+ def __getattr__(name):
353
+ if name == "chakra":
354
+ from reflex.utils import console
355
+
356
+ console.deprecate(
357
+ "rx.chakra",
358
+ reason="and moved to a separate package. "
359
+ "To continue using Chakra UI components, install the `reflex-chakra` package via `pip install "
360
+ "reflex-chakra`.",
361
+ deprecation_version="0.6.0",
362
+ removal_version="0.7.0",
363
+ dedupe=True,
364
+ )
365
+ import reflex_chakra as rc
366
+
367
+ return rc
368
+ return getattr(name)
reflex/__init__.pyi CHANGED
@@ -21,7 +21,6 @@ from .admin import AdminDash as AdminDash
21
21
  from .app import App as App
22
22
  from .app import UploadFile as UploadFile
23
23
  from .base import Base as Base
24
- from .components import chakra as chakra
25
24
  from .components import el as el
26
25
  from .components import lucide as lucide
27
26
  from .components import next as next
reflex/app.py CHANGED
@@ -125,8 +125,8 @@ def default_backend_exception_handler(exception: Exception) -> EventSpec:
125
125
  )
126
126
  if Toaster.is_used:
127
127
  return toast(
128
+ "An error occurred.",
128
129
  level="error",
129
- title="An error occurred.",
130
130
  description="<br/>".join(error_message),
131
131
  position="top-center",
132
132
  id="backend_error",
@@ -16,7 +16,6 @@ _SUBMODULES: set[str] = {
16
16
  "react_player",
17
17
  "sonner",
18
18
  "suneditor",
19
- "chakra",
20
19
  "el",
21
20
  "base",
22
21
  "recharts",
@@ -4,7 +4,6 @@
4
4
  # ------------------------------------------------------
5
5
 
6
6
  from . import base as base
7
- from . import chakra as chakra
8
7
  from . import core as core
9
8
  from . import datadisplay as datadisplay
10
9
  from . import el as el
@@ -70,7 +70,7 @@ def get_base_component_map() -> dict[str, Callable]:
70
70
  "a": lambda value: Link.create(value),
71
71
  "code": lambda value: Code.create(value),
72
72
  "codeblock": lambda value, **props: CodeBlock.create(
73
- value, margin_y="1em", **props
73
+ value, margin_y="1em", wrap_long_lines=True, **props
74
74
  ),
75
75
  }
76
76
 
@@ -138,6 +138,9 @@ class XAxis(Axis):
138
138
  # Ensures that all datapoints within a chart contribute to its domain calculation, even when they are hidden
139
139
  include_hidden: Var[bool] = Var.create_safe(False)
140
140
 
141
+ # The range of the axis. Work best in conjuction with allow_data_overflow.
142
+ domain: Var[List]
143
+
141
144
 
142
145
  class YAxis(Axis):
143
146
  """A YAxis component in Recharts."""
@@ -192,6 +192,7 @@ class XAxis(Axis):
192
192
  ] = None,
193
193
  x_axis_id: Optional[Union[Var[Union[int, str]], str, int]] = None,
194
194
  include_hidden: Optional[Union[Var[bool], bool]] = None,
195
+ domain: Optional[Union[Var[List], List]] = None,
195
196
  data_key: Optional[Union[Var[Union[int, str]], str, int]] = None,
196
197
  hide: Optional[Union[Var[bool], bool]] = None,
197
198
  width: Optional[Union[Var[Union[int, str]], str, int]] = None,
@@ -320,6 +321,7 @@ class XAxis(Axis):
320
321
  orientation: The orientation of axis 'top' | 'bottom'
321
322
  x_axis_id: The id of x-axis which is corresponding to the data.
322
323
  include_hidden: Ensures that all datapoints within a chart contribute to its domain calculation, even when they are hidden
324
+ domain: The range of the axis. Work best in conjuction with allow_data_overflow.
323
325
  data_key: The key of data displayed in the axis.
324
326
  hide: If set true, the axis do not display in the chart.
325
327
  width: The width of axis which is usually calculated internally.
@@ -4,6 +4,8 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any, ClassVar, Literal, Optional, Union
6
6
 
7
+ from pydantic import ValidationError
8
+
7
9
  from reflex.base import Base
8
10
  from reflex.components.component import Component, ComponentNamespace
9
11
  from reflex.components.lucide.icon import Icon
@@ -27,7 +29,6 @@ LiteralPosition = Literal[
27
29
  "bottom-right",
28
30
  ]
29
31
 
30
-
31
32
  toast_ref = Var.create_safe("refs['__toast']", _var_is_string=False)
32
33
 
33
34
 
@@ -128,6 +129,24 @@ class ToastProps(PropsBase):
128
129
  # Function that gets called when the toast disappears automatically after it's timeout (duration` prop).
129
130
  on_auto_close: Optional[Any]
130
131
 
132
+ def __init__(self, **kwargs):
133
+ """Initialize the props.
134
+
135
+ Args:
136
+ kwargs: Kwargs to initialize the props.
137
+
138
+ Raises:
139
+ ValueError: If invalid props are passed on instantiation.
140
+ """
141
+ try:
142
+ super().__init__(**kwargs)
143
+ except ValidationError as e:
144
+ invalid_fields = ", ".join([error["loc"][0] for error in e.errors()]) # type: ignore
145
+ supported_props_str = ", ".join(f'"{field}"' for field in self.get_fields())
146
+ raise ValueError(
147
+ f"Invalid prop(s) {invalid_fields} for rx.toast. Supported props are {supported_props_str}"
148
+ ) from None
149
+
131
150
  def dict(self, *args, **kwargs) -> dict[str, Any]:
132
151
  """Convert the object to a dictionary.
133
152
 
@@ -159,6 +178,13 @@ class ToastProps(PropsBase):
159
178
  )
160
179
  return d
161
180
 
181
+ class Config:
182
+ """Pydantic config."""
183
+
184
+ arbitrary_types_allowed = True
185
+ use_enum_values = True
186
+ extra = "forbid"
187
+
162
188
 
163
189
  class Toaster(Component):
164
190
  """A Toaster Component for displaying toast notifications."""
@@ -51,6 +51,11 @@ class ToastProps(PropsBase):
51
51
 
52
52
  def dict(self, *args, **kwargs) -> dict[str, Any]: ...
53
53
 
54
+ class Config:
55
+ arbitrary_types_allowed = True
56
+ use_enum_values = True
57
+ extra = "forbid"
58
+
54
59
  class Toaster(Component):
55
60
  is_used: ClassVar[bool] = False
56
61
 
reflex/constants/base.py CHANGED
@@ -77,6 +77,8 @@ class Reflex(SimpleNamespace):
77
77
  os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
78
78
  )
79
79
 
80
+ RELEASES_URL = f"https://api.github.com/repos/reflex-dev/templates/releases"
81
+
80
82
 
81
83
  class ReflexHostingCLI(SimpleNamespace):
82
84
  """Base constants concerning Reflex Hosting CLI."""
@@ -51,6 +51,8 @@ class Bun(SimpleNamespace):
51
51
  WINDOWS_INSTALL_URL = (
52
52
  "https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/install.ps1"
53
53
  )
54
+ # Path of the bunfig file
55
+ CONFIG_PATH = "bunfig.toml"
54
56
 
55
57
 
56
58
  # FNM config.
@@ -7,7 +7,7 @@ from reflex.vars import Var, VarData
7
7
 
8
8
 
9
9
  def _compose_react_imports(tags: list[str]) -> dict[str, list[ImportVar]]:
10
- return {"react": [ImportVar(tag=tag, install=False) for tag in tags]}
10
+ return {"react": [ImportVar(tag=tag) for tag in tags]}
11
11
 
12
12
 
13
13
  def const(name, value) -> Var:
reflex/reflex.py CHANGED
@@ -85,10 +85,6 @@ def _init(
85
85
  prerequisites.initialize_reflex_user_directory()
86
86
  prerequisites.ensure_reflex_installation_id()
87
87
 
88
- # When upgrading to 0.4, show migration instructions.
89
- if prerequisites.should_show_rx_chakra_migration_instructions():
90
- prerequisites.show_rx_chakra_migration_instructions()
91
-
92
88
  # Set up the web project.
93
89
  prerequisites.initialize_frontend_dependencies()
94
90
 
@@ -458,17 +454,6 @@ def makemigrations(
458
454
  )
459
455
 
460
456
 
461
- @script_cli.command(
462
- name="keep-chakra",
463
- help="Change all rx.<component> references to rx.chakra.<component>, to preserve Chakra UI usage.",
464
- )
465
- def keep_chakra():
466
- """Change all rx.<component> references to rx.chakra.<component>, to preserve Chakra UI usage."""
467
- from reflex.utils import prerequisites
468
-
469
- prerequisites.migrate_to_rx_chakra()
470
-
471
-
472
457
  @cli.command()
473
458
  def deploy(
474
459
  key: Optional[str] = typer.Option(
reflex/state.py CHANGED
@@ -55,6 +55,7 @@ from reflex.utils import console, format, prerequisites, types
55
55
  from reflex.utils.exceptions import ImmutableStateError, LockExpiredError
56
56
  from reflex.utils.exec import is_testing_env
57
57
  from reflex.utils.serializers import SerializedType, serialize, serializer
58
+ from reflex.utils.types import override
58
59
  from reflex.vars import BaseVar, ComputedVar, Var, computed_var
59
60
 
60
61
  if TYPE_CHECKING:
@@ -1232,6 +1233,17 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1232
1233
  parent_states_with_name.append((parent_state.get_full_name(), parent_state))
1233
1234
  return parent_states_with_name
1234
1235
 
1236
+ def _get_root_state(self) -> BaseState:
1237
+ """Get the root state of the state tree.
1238
+
1239
+ Returns:
1240
+ The root state of the state tree.
1241
+ """
1242
+ parent_state = self
1243
+ while parent_state.parent_state is not None:
1244
+ parent_state = parent_state.parent_state
1245
+ return parent_state
1246
+
1235
1247
  async def _populate_parent_states(self, target_state_cls: Type[BaseState]):
1236
1248
  """Populate substates in the tree between the target_state_cls and common ancestor of this state.
1237
1249
 
@@ -1291,10 +1303,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1291
1303
  Returns:
1292
1304
  The instance of state_cls associated with this state's client_token.
1293
1305
  """
1294
- if self.parent_state is None:
1295
- root_state = self
1296
- else:
1297
- root_state = self._get_parent_states()[-1][1]
1306
+ root_state = self._get_root_state()
1298
1307
  return root_state.get_substate(state_cls.get_full_name().split("."))
1299
1308
 
1300
1309
  async def _get_state_from_redis(self, state_cls: Type[BaseState]) -> BaseState:
@@ -1445,9 +1454,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1445
1454
  The valid StateUpdate containing the events and final flag.
1446
1455
  """
1447
1456
  # get the delta from the root of the state tree
1448
- state = self
1449
- while state.parent_state is not None:
1450
- state = state.parent_state
1457
+ state = self._get_root_state()
1451
1458
 
1452
1459
  token = self.router.session.client_token
1453
1460
 
@@ -2368,6 +2375,7 @@ class StateManagerMemory(StateManager):
2368
2375
  "_states_locks": {"exclude": True},
2369
2376
  }
2370
2377
 
2378
+ @override
2371
2379
  async def get_state(self, token: str) -> BaseState:
2372
2380
  """Get the state for a token.
2373
2381
 
@@ -2383,6 +2391,7 @@ class StateManagerMemory(StateManager):
2383
2391
  self.states[token] = self.state(_reflex_internal_init=True)
2384
2392
  return self.states[token]
2385
2393
 
2394
+ @override
2386
2395
  async def set_state(self, token: str, state: BaseState):
2387
2396
  """Set the state for a token.
2388
2397
 
@@ -2392,6 +2401,7 @@ class StateManagerMemory(StateManager):
2392
2401
  """
2393
2402
  pass
2394
2403
 
2404
+ @override
2395
2405
  @contextlib.asynccontextmanager
2396
2406
  async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
2397
2407
  """Modify the state for a token while holding exclusive lock.
@@ -2483,19 +2493,6 @@ class StateManagerRedis(StateManager):
2483
2493
  # Only warn about each state class size once.
2484
2494
  _warned_about_state_size: ClassVar[Set[str]] = set()
2485
2495
 
2486
- def _get_root_state(self, state: BaseState) -> BaseState:
2487
- """Chase parent_state pointers to find an instance of the top-level state.
2488
-
2489
- Args:
2490
- state: The state to start from.
2491
-
2492
- Returns:
2493
- An instance of the top-level state (self.state).
2494
- """
2495
- while type(state) != self.state and state.parent_state is not None:
2496
- state = state.parent_state
2497
- return state
2498
-
2499
2496
  async def _get_parent_state(self, token: str) -> BaseState | None:
2500
2497
  """Get the parent state for the state requested in the token.
2501
2498
 
@@ -2558,6 +2555,7 @@ class StateManagerRedis(StateManager):
2558
2555
  for substate_name, substate_task in tasks.items():
2559
2556
  state.substates[substate_name] = await substate_task
2560
2557
 
2558
+ @override
2561
2559
  async def get_state(
2562
2560
  self,
2563
2561
  token: str,
@@ -2609,7 +2607,7 @@ class StateManagerRedis(StateManager):
2609
2607
  # To retain compatibility with previous implementation, by default, we return
2610
2608
  # the top-level state by chasing `parent_state` pointers up the tree.
2611
2609
  if top_level:
2612
- return self._get_root_state(state)
2610
+ return state._get_root_state()
2613
2611
  return state
2614
2612
 
2615
2613
  # TODO: dedupe the following logic with the above block
@@ -2631,7 +2629,7 @@ class StateManagerRedis(StateManager):
2631
2629
  # To retain compatibility with previous implementation, by default, we return
2632
2630
  # the top-level state by chasing `parent_state` pointers up the tree.
2633
2631
  if top_level:
2634
- return self._get_root_state(state)
2632
+ return state._get_root_state()
2635
2633
  return state
2636
2634
 
2637
2635
  def _warn_if_too_large(
@@ -2657,6 +2655,7 @@ class StateManagerRedis(StateManager):
2657
2655
  )
2658
2656
  self._warned_about_state_size.add(state_full_name)
2659
2657
 
2658
+ @override
2660
2659
  async def set_state(
2661
2660
  self,
2662
2661
  token: str,
@@ -2717,6 +2716,7 @@ class StateManagerRedis(StateManager):
2717
2716
  for t in tasks:
2718
2717
  await t
2719
2718
 
2719
+ @override
2720
2720
  @contextlib.asynccontextmanager
2721
2721
  async def modify_state(self, token: str) -> AsyncIterator[BaseState]:
2722
2722
  """Modify the state for a token while holding exclusive lock.
reflex/testing.py CHANGED
@@ -115,7 +115,7 @@ class AppHarness:
115
115
 
116
116
  app_name: str
117
117
  app_source: Optional[
118
- types.FunctionType | types.ModuleType | str | functools.partial
118
+ types.FunctionType | types.ModuleType | str | functools.partial[Any]
119
119
  ]
120
120
  app_path: pathlib.Path
121
121
  app_module_path: pathlib.Path
@@ -134,7 +134,9 @@ class AppHarness:
134
134
  def create(
135
135
  cls,
136
136
  root: pathlib.Path,
137
- app_source: Optional[types.FunctionType | types.ModuleType | str] = None,
137
+ app_source: Optional[
138
+ types.FunctionType | types.ModuleType | str | functools.partial[Any]
139
+ ] = None,
138
140
  app_name: Optional[str] = None,
139
141
  ) -> "AppHarness":
140
142
  """Create an AppHarness instance at root.
@@ -274,7 +276,10 @@ class AppHarness:
274
276
  before_decorated_pages = reflex.app.DECORATED_PAGES[self.app_name].copy()
275
277
  # Ensure the AppHarness test does not skip State assignment due to running via pytest
276
278
  os.environ.pop(reflex.constants.PYTEST_CURRENT_TEST, None)
277
- self.app_module = reflex.utils.prerequisites.get_compiled_app(reload=True)
279
+ self.app_module = reflex.utils.prerequisites.get_compiled_app(
280
+ # Do not reload the module for pre-existing apps (only apps generated from source)
281
+ reload=self.app_source is not None
282
+ )
278
283
  # Save the pages that were added during testing
279
284
  self._decorated_pages = [
280
285
  p
reflex/utils/net.py ADDED
@@ -0,0 +1,43 @@
1
+ """Helpers for downloading files from the network."""
2
+
3
+ import os
4
+
5
+ import httpx
6
+
7
+ from . import console
8
+
9
+
10
+ def _httpx_verify_kwarg() -> bool:
11
+ """Get the value of the HTTPX verify keyword argument.
12
+
13
+ Returns:
14
+ True if SSL verification is enabled, False otherwise
15
+ """
16
+ ssl_no_verify = os.environ.get("SSL_NO_VERIFY", "").lower() in ["true", "1", "yes"]
17
+ return not ssl_no_verify
18
+
19
+
20
+ def get(url: str, **kwargs) -> httpx.Response:
21
+ """Make an HTTP GET request.
22
+
23
+ Args:
24
+ url: The URL to request.
25
+ **kwargs: Additional keyword arguments to pass to httpx.get.
26
+
27
+ Returns:
28
+ The response object.
29
+
30
+ Raises:
31
+ httpx.ConnectError: If the connection cannot be established.
32
+ """
33
+ kwargs.setdefault("verify", _httpx_verify_kwarg())
34
+ try:
35
+ return httpx.get(url, **kwargs)
36
+ except httpx.ConnectError as err:
37
+ if "CERTIFICATE_VERIFY_FAILED" in str(err):
38
+ # If the error is a certificate verification error, recommend mitigating steps.
39
+ console.error(
40
+ f"Certificate verification failed for {url}. Set environment variable SSL_CERT_FILE to the "
41
+ "path of the certificate file or SSL_NO_VERIFY=1 to disable verification."
42
+ )
43
+ raise