reflex 0.7.14a6__py3-none-any.whl → 0.8.0__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 (237) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +21 -11
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
  7. reflex/.templates/jinja/web/styles/styles.css.jinja2 +1 -0
  8. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -8
  9. reflex/.templates/web/app/entry.client.js +8 -0
  10. reflex/.templates/web/app/routes.js +10 -0
  11. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -37
  12. reflex/.templates/web/postcss.config.js +1 -1
  13. reflex/.templates/web/react-router.config.js +6 -0
  14. reflex/.templates/web/styles/__reflex_style_reset.css +399 -0
  15. reflex/.templates/web/utils/client_side_routing.js +21 -19
  16. reflex/.templates/web/utils/react-theme.js +92 -0
  17. reflex/.templates/web/utils/state.js +251 -100
  18. reflex/.templates/web/vite-plugin-safari-cachebust.js +160 -0
  19. reflex/.templates/web/vite.config.js +39 -0
  20. reflex/__init__.py +1 -6
  21. reflex/__init__.pyi +327 -192
  22. reflex/app.py +86 -135
  23. reflex/base.py +1 -87
  24. reflex/compiler/compiler.py +70 -19
  25. reflex/compiler/templates.py +3 -3
  26. reflex/compiler/utils.py +91 -33
  27. reflex/components/__init__.py +0 -2
  28. reflex/components/__init__.pyi +34 -18
  29. reflex/components/base/__init__.py +1 -5
  30. reflex/components/base/__init__.pyi +30 -21
  31. reflex/components/base/app_wrap.pyi +7 -7
  32. reflex/components/base/body.pyi +7 -7
  33. reflex/components/base/document.py +18 -14
  34. reflex/components/base/document.pyi +88 -38
  35. reflex/components/base/error_boundary.pyi +7 -7
  36. reflex/components/base/fragment.pyi +7 -7
  37. reflex/components/base/link.pyi +12 -12
  38. reflex/components/base/meta.py +4 -15
  39. reflex/components/base/meta.pyi +31 -31
  40. reflex/components/base/script.py +60 -58
  41. reflex/components/base/script.pyi +248 -34
  42. reflex/components/base/strict_mode.pyi +7 -7
  43. reflex/components/component.py +146 -217
  44. reflex/components/core/__init__.py +1 -0
  45. reflex/components/core/__init__.pyi +77 -37
  46. reflex/components/core/auto_scroll.pyi +7 -7
  47. reflex/components/core/banner.pyi +33 -33
  48. reflex/components/core/client_side_routing.py +7 -6
  49. reflex/components/core/client_side_routing.pyi +8 -59
  50. reflex/components/core/clipboard.pyi +7 -7
  51. reflex/components/core/debounce.py +1 -0
  52. reflex/components/core/debounce.pyi +7 -7
  53. reflex/components/core/foreach.py +5 -4
  54. reflex/components/core/helmet.py +14 -0
  55. reflex/components/{next/base.pyi → core/helmet.pyi} +12 -10
  56. reflex/components/core/html.pyi +7 -7
  57. reflex/components/core/match.py +3 -3
  58. reflex/components/core/sticky.pyi +21 -20
  59. reflex/components/core/upload.py +4 -2
  60. reflex/components/core/upload.pyi +26 -25
  61. reflex/components/datadisplay/__init__.pyi +13 -7
  62. reflex/components/datadisplay/code.py +14 -79
  63. reflex/components/datadisplay/code.pyi +11 -13
  64. reflex/components/datadisplay/dataeditor.pyi +38 -15
  65. reflex/components/datadisplay/shiki_code_block.py +5 -3
  66. reflex/components/datadisplay/shiki_code_block.pyi +16 -15
  67. reflex/components/dynamic.py +5 -5
  68. reflex/components/el/__init__.pyi +506 -246
  69. reflex/components/el/element.pyi +7 -7
  70. reflex/components/el/elements/__init__.pyi +504 -245
  71. reflex/components/el/elements/base.pyi +7 -7
  72. reflex/components/el/elements/forms.pyi +146 -101
  73. reflex/components/el/elements/inline.pyi +142 -142
  74. reflex/components/el/elements/media.pyi +131 -130
  75. reflex/components/el/elements/metadata.pyi +32 -32
  76. reflex/components/el/elements/other.pyi +37 -37
  77. reflex/components/el/elements/scripts.pyi +17 -17
  78. reflex/components/el/elements/sectioning.pyi +77 -77
  79. reflex/components/el/elements/tables.pyi +52 -52
  80. reflex/components/el/elements/typography.pyi +77 -77
  81. reflex/components/field.py +175 -0
  82. reflex/components/gridjs/datatable.py +2 -2
  83. reflex/components/gridjs/datatable.pyi +14 -14
  84. reflex/components/lucide/icon.py +6 -2
  85. reflex/components/lucide/icon.pyi +19 -17
  86. reflex/components/markdown/markdown.py +5 -3
  87. reflex/components/markdown/markdown.pyi +7 -7
  88. reflex/components/moment/moment.py +1 -1
  89. reflex/components/moment/moment.pyi +7 -7
  90. reflex/components/plotly/plotly.py +12 -6
  91. reflex/components/plotly/plotly.pyi +50 -49
  92. reflex/components/props.py +376 -27
  93. reflex/components/radix/__init__.pyi +123 -65
  94. reflex/components/radix/primitives/__init__.pyi +6 -4
  95. reflex/components/radix/primitives/accordion.py +8 -1
  96. reflex/components/radix/primitives/accordion.pyi +37 -37
  97. reflex/components/radix/primitives/base.pyi +12 -12
  98. reflex/components/radix/primitives/drawer.pyi +56 -55
  99. reflex/components/radix/primitives/form.pyi +63 -53
  100. reflex/components/radix/primitives/progress.pyi +26 -25
  101. reflex/components/radix/primitives/slider.pyi +27 -27
  102. reflex/components/radix/themes/__init__.pyi +5 -6
  103. reflex/components/radix/themes/base.py +3 -3
  104. reflex/components/radix/themes/base.pyi +42 -42
  105. reflex/components/radix/themes/color_mode.py +5 -6
  106. reflex/components/radix/themes/color_mode.pyi +17 -17
  107. reflex/components/radix/themes/components/__init__.pyi +75 -38
  108. reflex/components/radix/themes/components/alert_dialog.pyi +37 -37
  109. reflex/components/radix/themes/components/aspect_ratio.pyi +7 -7
  110. reflex/components/radix/themes/components/avatar.pyi +7 -7
  111. reflex/components/radix/themes/components/badge.pyi +7 -7
  112. reflex/components/radix/themes/components/button.pyi +7 -7
  113. reflex/components/radix/themes/components/callout.pyi +26 -25
  114. reflex/components/radix/themes/components/card.pyi +7 -7
  115. reflex/components/radix/themes/components/checkbox.pyi +16 -15
  116. reflex/components/radix/themes/components/checkbox_cards.pyi +12 -12
  117. reflex/components/radix/themes/components/checkbox_group.pyi +12 -12
  118. reflex/components/radix/themes/components/context_menu.pyi +67 -67
  119. reflex/components/radix/themes/components/data_list.pyi +22 -22
  120. reflex/components/radix/themes/components/dialog.pyi +36 -35
  121. reflex/components/radix/themes/components/dropdown_menu.pyi +42 -42
  122. reflex/components/radix/themes/components/hover_card.pyi +21 -20
  123. reflex/components/radix/themes/components/icon_button.pyi +7 -7
  124. reflex/components/radix/themes/components/inset.pyi +7 -7
  125. reflex/components/radix/themes/components/popover.pyi +22 -22
  126. reflex/components/radix/themes/components/progress.pyi +7 -7
  127. reflex/components/radix/themes/components/radio.pyi +7 -7
  128. reflex/components/radix/themes/components/radio_cards.pyi +12 -12
  129. reflex/components/radix/themes/components/radio_group.pyi +21 -20
  130. reflex/components/radix/themes/components/scroll_area.pyi +7 -7
  131. reflex/components/radix/themes/components/segmented_control.pyi +12 -12
  132. reflex/components/radix/themes/components/select.pyi +46 -45
  133. reflex/components/radix/themes/components/separator.pyi +7 -7
  134. reflex/components/radix/themes/components/skeleton.pyi +7 -7
  135. reflex/components/radix/themes/components/slider.pyi +17 -9
  136. reflex/components/radix/themes/components/spinner.pyi +7 -7
  137. reflex/components/radix/themes/components/switch.pyi +7 -7
  138. reflex/components/radix/themes/components/table.pyi +37 -37
  139. reflex/components/radix/themes/components/tabs.pyi +26 -25
  140. reflex/components/radix/themes/components/text_area.pyi +15 -9
  141. reflex/components/radix/themes/components/text_field.pyi +32 -19
  142. reflex/components/radix/themes/components/tooltip.pyi +7 -7
  143. reflex/components/radix/themes/layout/__init__.pyi +27 -14
  144. reflex/components/radix/themes/layout/base.pyi +7 -7
  145. reflex/components/radix/themes/layout/box.pyi +7 -7
  146. reflex/components/radix/themes/layout/center.pyi +7 -7
  147. reflex/components/radix/themes/layout/container.pyi +7 -7
  148. reflex/components/radix/themes/layout/flex.pyi +7 -7
  149. reflex/components/radix/themes/layout/grid.pyi +7 -7
  150. reflex/components/radix/themes/layout/list.pyi +26 -25
  151. reflex/components/radix/themes/layout/section.pyi +7 -7
  152. reflex/components/radix/themes/layout/spacer.pyi +7 -7
  153. reflex/components/radix/themes/layout/stack.pyi +17 -17
  154. reflex/components/radix/themes/typography/__init__.pyi +7 -5
  155. reflex/components/radix/themes/typography/blockquote.pyi +7 -7
  156. reflex/components/radix/themes/typography/code.pyi +7 -7
  157. reflex/components/radix/themes/typography/heading.pyi +7 -7
  158. reflex/components/radix/themes/typography/link.py +46 -11
  159. reflex/components/radix/themes/typography/link.pyi +312 -9
  160. reflex/components/radix/themes/typography/text.pyi +36 -35
  161. reflex/components/react_player/audio.pyi +10 -8
  162. reflex/components/react_player/react_player.pyi +7 -7
  163. reflex/components/react_player/video.pyi +10 -8
  164. reflex/components/recharts/__init__.pyi +208 -100
  165. reflex/components/recharts/cartesian.py +10 -8
  166. reflex/components/recharts/cartesian.pyi +90 -94
  167. reflex/components/recharts/charts.py +4 -2
  168. reflex/components/recharts/charts.pyi +49 -49
  169. reflex/components/recharts/general.pyi +31 -31
  170. reflex/components/recharts/polar.py +8 -4
  171. reflex/components/recharts/polar.pyi +23 -23
  172. reflex/components/recharts/recharts.py +2 -2
  173. reflex/components/recharts/recharts.pyi +12 -12
  174. reflex/components/sonner/toast.py +3 -3
  175. reflex/components/sonner/toast.pyi +9 -9
  176. reflex/config.py +10 -113
  177. reflex/constants/__init__.py +2 -2
  178. reflex/constants/base.py +28 -11
  179. reflex/constants/compiler.py +12 -3
  180. reflex/constants/event.py +1 -0
  181. reflex/constants/installer.py +26 -20
  182. reflex/constants/route.py +27 -8
  183. reflex/constants/state.py +2 -0
  184. reflex/custom_components/custom_components.py +0 -14
  185. reflex/environment.py +77 -5
  186. reflex/event.py +178 -81
  187. reflex/experimental/__init__.py +0 -30
  188. reflex/istate/__init__.py +69 -0
  189. reflex/istate/manager.py +1 -0
  190. reflex/istate/proxy.py +5 -3
  191. reflex/page.py +0 -27
  192. reflex/plugins/__init__.py +3 -2
  193. reflex/plugins/base.py +5 -1
  194. reflex/plugins/shared_tailwind.py +215 -0
  195. reflex/plugins/sitemap.py +206 -0
  196. reflex/plugins/tailwind_v3.py +15 -108
  197. reflex/plugins/tailwind_v4.py +18 -110
  198. reflex/reflex.py +1 -0
  199. reflex/route.py +157 -75
  200. reflex/state.py +171 -155
  201. reflex/testing.py +86 -16
  202. reflex/utils/build.py +38 -82
  203. reflex/utils/exec.py +83 -175
  204. reflex/utils/export.py +2 -2
  205. reflex/utils/format.py +1 -5
  206. reflex/utils/imports.py +5 -16
  207. reflex/utils/misc.py +67 -0
  208. reflex/utils/prerequisites.py +66 -68
  209. reflex/utils/processes.py +24 -47
  210. reflex/utils/pyi_generator.py +44 -49
  211. reflex/utils/serializers.py +14 -1
  212. reflex/utils/telemetry.py +0 -15
  213. reflex/utils/types.py +197 -62
  214. reflex/vars/__init__.py +2 -0
  215. reflex/vars/base.py +367 -134
  216. {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/METADATA +15 -8
  217. reflex-0.8.0.dist-info/RECORD +403 -0
  218. reflex/.templates/web/next.config.js +0 -7
  219. reflex/components/base/head.py +0 -20
  220. reflex/components/base/head.pyi +0 -116
  221. reflex/components/next/__init__.py +0 -10
  222. reflex/components/next/base.py +0 -7
  223. reflex/components/next/image.py +0 -117
  224. reflex/components/next/image.pyi +0 -94
  225. reflex/components/next/link.py +0 -20
  226. reflex/components/next/link.pyi +0 -67
  227. reflex/components/next/video.py +0 -38
  228. reflex/components/next/video.pyi +0 -68
  229. reflex/components/suneditor/__init__.py +0 -5
  230. reflex/components/suneditor/editor.py +0 -269
  231. reflex/components/suneditor/editor.pyi +0 -199
  232. reflex/experimental/layout.py +0 -254
  233. reflex/experimental/layout.pyi +0 -814
  234. reflex-0.7.14a6.dist-info/RECORD +0 -408
  235. {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/WHEEL +0 -0
  236. {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/entry_points.txt +0 -0
  237. {reflex-0.7.14a6.dist-info → reflex-0.8.0.dist-info}/licenses/LICENSE +0 -0
reflex/app.py CHANGED
@@ -13,7 +13,14 @@ import io
13
13
  import json
14
14
  import sys
15
15
  import traceback
16
- from collections.abc import AsyncIterator, Callable, Coroutine, Sequence
16
+ from collections.abc import (
17
+ AsyncGenerator,
18
+ AsyncIterator,
19
+ Callable,
20
+ Coroutine,
21
+ Mapping,
22
+ Sequence,
23
+ )
17
24
  from datetime import datetime
18
25
  from pathlib import Path
19
26
  from timeit import default_timer as timer
@@ -32,7 +39,6 @@ from starlette.middleware import cors
32
39
  from starlette.requests import ClientDisconnect, Request
33
40
  from starlette.responses import JSONResponse, Response, StreamingResponse
34
41
  from starlette.staticfiles import StaticFiles
35
- from typing_extensions import deprecated
36
42
 
37
43
  from reflex import constants
38
44
  from reflex.admin import AdminDash
@@ -61,7 +67,7 @@ from reflex.components.core.banner import (
61
67
  )
62
68
  from reflex.components.core.breakpoints import set_breakpoints
63
69
  from reflex.components.core.client_side_routing import (
64
- Default404Page,
70
+ default_404_page,
65
71
  wait_for_client_redirect,
66
72
  )
67
73
  from reflex.components.core.sticky import sticky
@@ -78,6 +84,7 @@ from reflex.event import (
78
84
  EventType,
79
85
  IndividualEventType,
80
86
  get_hydrate_event,
87
+ noop,
81
88
  )
82
89
  from reflex.model import Model, get_db_status
83
90
  from reflex.page import DECORATED_PAGES
@@ -214,17 +221,21 @@ def default_overlay_component() -> Component:
214
221
  return Fragment.create(memo(default_overlay_components)())
215
222
 
216
223
 
217
- def default_error_boundary(*children: Component) -> Component:
224
+ def default_error_boundary(*children: Component, **props) -> Component:
218
225
  """Default error_boundary attribute for App.
219
226
 
220
227
  Args:
221
228
  *children: The children to render in the error boundary.
229
+ **props: The props to pass to the error boundary.
222
230
 
223
231
  Returns:
224
232
  The default error_boundary, which is an ErrorBoundary.
225
233
 
226
234
  """
227
- return ErrorBoundary.create(*children)
235
+ return ErrorBoundary.create(
236
+ *children,
237
+ **props,
238
+ )
228
239
 
229
240
 
230
241
  class OverlayFragment(Fragment):
@@ -246,8 +257,6 @@ class UploadFile(StarletteUploadFile):
246
257
 
247
258
  path: Path | None = dataclasses.field(default=None)
248
259
 
249
- _deprecated_filename: str | None = dataclasses.field(default=None)
250
-
251
260
  size: int | None = dataclasses.field(default=None)
252
261
 
253
262
  headers: Headers = dataclasses.field(default_factory=Headers)
@@ -263,21 +272,6 @@ class UploadFile(StarletteUploadFile):
263
272
  return self.path.name
264
273
  return None
265
274
 
266
- @property
267
- def filename(self) -> str | None:
268
- """Get the filename of the uploaded file.
269
-
270
- Returns:
271
- The filename of the uploaded file.
272
- """
273
- console.deprecate(
274
- feature_name="UploadFile.filename",
275
- reason="Use UploadFile.name instead.",
276
- deprecation_version="0.7.1",
277
- removal_version="0.8.0",
278
- )
279
- return self._deprecated_filename
280
-
281
275
 
282
276
  @dataclasses.dataclass(
283
277
  frozen=True,
@@ -291,8 +285,8 @@ class UnevaluatedPage:
291
285
  description: Var | str | None
292
286
  image: str
293
287
  on_load: EventType[()] | None
294
- meta: list[dict[str, str]]
295
- context: dict[str, Any] | None
288
+ meta: Sequence[Mapping[str, str]]
289
+ context: Mapping[str, Any]
296
290
 
297
291
  def merged_with(self, other: UnevaluatedPage) -> UnevaluatedPage:
298
292
  """Merge the other page into this one.
@@ -350,20 +344,22 @@ class App(MiddlewareMixin, LifespanMixin):
350
344
  # A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app.
351
345
  stylesheets: list[str] = dataclasses.field(default_factory=list)
352
346
 
347
+ # Whether to include CSS reset for margin and padding (defaults to True).
348
+ reset_style: bool = dataclasses.field(default=True)
349
+
353
350
  # A component that is present on every page (defaults to the Connection Error banner).
354
351
  overlay_component: Component | ComponentCallable | None = dataclasses.field(
355
352
  default=None
356
353
  )
357
354
 
358
- # Error boundary component to wrap the app with.
359
- error_boundary: ComponentCallable | None = dataclasses.field(default=None)
360
-
361
355
  # App wraps to be applied to the whole app. Expected to be a dictionary of (order, name) to a function that takes whether the state is enabled and optionally returns a component.
362
356
  app_wraps: dict[tuple[int, str], Callable[[bool], Component | None]] = (
363
357
  dataclasses.field(
364
358
  default_factory=lambda: {
365
359
  (55, "ErrorBoundary"): (
366
- lambda stateful: default_error_boundary() if stateful else None
360
+ lambda stateful: default_error_boundary(
361
+ **({"on_error": noop()} if not stateful else {})
362
+ )
367
363
  ),
368
364
  (5, "Overlay"): (
369
365
  lambda stateful: default_overlay_component() if stateful else None
@@ -443,24 +439,6 @@ class App(MiddlewareMixin, LifespanMixin):
443
439
  # FastAPI app for compatibility with FastAPI.
444
440
  _cached_fastapi_app: FastAPI | None = None
445
441
 
446
- @property
447
- @deprecated("Use `api_transformer=your_fastapi_app` instead.")
448
- def api(self) -> FastAPI:
449
- """Get the backend api.
450
-
451
- Returns:
452
- The backend api.
453
- """
454
- if self._cached_fastapi_app is None:
455
- self._cached_fastapi_app = FastAPI()
456
- console.deprecate(
457
- feature_name="App.api",
458
- reason="Set `api_transformer=your_fastapi_app` instead.",
459
- deprecation_version="0.7.9",
460
- removal_version="0.8.0",
461
- )
462
- return self._cached_fastapi_app
463
-
464
442
  @property
465
443
  def event_namespace(self) -> EventNamespace | None:
466
444
  """Get the event namespace.
@@ -619,7 +597,7 @@ class App(MiddlewareMixin, LifespanMixin):
619
597
  self._apply_decorated_pages()
620
598
 
621
599
  compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit(
622
- self._compile
600
+ self._compile, prerender_routes=is_prod_mode()
623
601
  )
624
602
 
625
603
  def callback(f: concurrent.futures.Future):
@@ -794,13 +772,13 @@ class App(MiddlewareMixin, LifespanMixin):
794
772
  msg = "Route must be set if component is not a callable."
795
773
  raise exceptions.RouteValueError(msg)
796
774
  # Format the route.
797
- route = format.format_route(component.__name__)
775
+ route = format.format_route(format.to_kebab_case(component.__name__))
798
776
  else:
799
- route = format.format_route(route, format_case=False)
777
+ route = format.format_route(route)
800
778
 
801
779
  if route == constants.Page404.SLUG:
802
780
  if component is None:
803
- component = Default404Page.create()
781
+ component = default_404_page
804
782
  component = wait_for_client_redirect(self._generate_component(component))
805
783
  title = title or constants.Page404.TITLE
806
784
  description = description or constants.Page404.DESCRIPTION
@@ -821,7 +799,7 @@ class App(MiddlewareMixin, LifespanMixin):
821
799
  image=image,
822
800
  on_load=on_load,
823
801
  meta=meta,
824
- context=context,
802
+ context=context or {},
825
803
  )
826
804
 
827
805
  if route in self._unevaluated_pages:
@@ -851,10 +829,11 @@ class App(MiddlewareMixin, LifespanMixin):
851
829
  state = self._state if self._state else State
852
830
  state.setup_dynamic_args(get_route_args(route))
853
831
 
854
- if on_load:
855
- self._load_events[route] = (
856
- on_load if isinstance(on_load, list) else [on_load]
857
- )
832
+ self._load_events[route] = (
833
+ (on_load if isinstance(on_load, list) else [on_load])
834
+ if on_load is not None
835
+ else []
836
+ )
858
837
 
859
838
  self._unevaluated_pages[route] = unevaluated_page
860
839
 
@@ -883,24 +862,40 @@ class App(MiddlewareMixin, LifespanMixin):
883
862
  if save_page:
884
863
  self._pages[route] = component
885
864
 
886
- def get_load_events(self, route: str) -> list[IndividualEventType[()]]:
865
+ @functools.cached_property
866
+ def router(self) -> Callable[[str], str | None]:
867
+ """Get the route computer function.
868
+
869
+ Returns:
870
+ The route computer function.
871
+ """
872
+ from reflex.route import get_router
873
+
874
+ return get_router(list(self._unevaluated_pages))
875
+
876
+ def get_load_events(self, path: str) -> list[IndividualEventType[()]]:
887
877
  """Get the load events for a route.
888
878
 
889
879
  Args:
890
- route: The route to get the load events for.
880
+ path: The route to get the load events for.
891
881
 
892
882
  Returns:
893
883
  The load events for the route.
894
884
  """
895
- route = route.lstrip("/")
896
- if route == "":
897
- route = constants.PageNames.INDEX_ROUTE
898
- return self._load_events.get(route, [])
885
+ four_oh_four_load_events = self._load_events.get("404", [])
886
+ route = self.router(path)
887
+ if not route:
888
+ # If the path is not a valid route, return the 404 page load events.
889
+ return four_oh_four_load_events
890
+ return self._load_events.get(
891
+ route,
892
+ four_oh_four_load_events,
893
+ )
899
894
 
900
895
  def _check_routes_conflict(self, new_route: str):
901
896
  """Verify if there is any conflict between the new route and any existing route.
902
897
 
903
- Based on conflicts that NextJS would throw if not intercepted.
898
+ Based on conflicts that React Router would throw if not intercepted.
904
899
 
905
900
  Raises:
906
901
  RouteValueError: exception showing which conflict exist with the route to be added
@@ -916,7 +911,6 @@ class App(MiddlewareMixin, LifespanMixin):
916
911
  segments = (
917
912
  constants.RouteRegex.SINGLE_SEGMENT,
918
913
  constants.RouteRegex.DOUBLE_SEGMENT,
919
- constants.RouteRegex.SINGLE_CATCHALL_SEGMENT,
920
914
  constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT,
921
915
  )
922
916
  for route in self._pages:
@@ -938,44 +932,6 @@ class App(MiddlewareMixin, LifespanMixin):
938
932
  # info1 will break away into its own tree.
939
933
  break
940
934
 
941
- def add_custom_404_page(
942
- self,
943
- component: Component | ComponentCallable | None = None,
944
- title: str = constants.Page404.TITLE,
945
- image: str = constants.Page404.IMAGE,
946
- description: str = constants.Page404.DESCRIPTION,
947
- on_load: EventType[()] | None = None,
948
- meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
949
- ):
950
- """Define a custom 404 page for any url having no match.
951
-
952
- If there is no page defined on 'index' route, add the 404 page to it.
953
- If there is no global catchall defined, add the 404 page with a catchall.
954
-
955
- Args:
956
- component: The component to display at the page.
957
- title: The title of the page.
958
- image: The image to display on the page.
959
- description: The description of the page.
960
- on_load: The event handler(s) that will be called each time the page load.
961
- meta: The metadata of the page.
962
- """
963
- console.deprecate(
964
- feature_name="App.add_custom_404_page",
965
- reason=f"Use app.add_page(component, route='/{constants.Page404.SLUG}') instead.",
966
- deprecation_version="0.6.7",
967
- removal_version="0.8.0",
968
- )
969
- self.add_page(
970
- component=component,
971
- route=constants.Page404.SLUG,
972
- title=title or constants.Page404.TITLE,
973
- image=image or constants.Page404.IMAGE,
974
- description=description or constants.Page404.DESCRIPTION,
975
- on_load=on_load,
976
- meta=meta,
977
- )
978
-
979
935
  def _setup_admin_dash(self):
980
936
  """Setup the admin dash."""
981
937
  try:
@@ -1024,7 +980,7 @@ class App(MiddlewareMixin, LifespanMixin):
1024
980
  for i, tags in imports.items()
1025
981
  if i not in dependencies
1026
982
  and i not in dev_dependencies
1027
- and not any(i.startswith(prefix) for prefix in ["/", "$/", ".", "next/"])
983
+ and not any(i.startswith(prefix) for prefix in ["/", "$/", "."])
1028
984
  and i != ""
1029
985
  and any(tag.install for tag in tags)
1030
986
  }
@@ -1148,17 +1104,17 @@ class App(MiddlewareMixin, LifespanMixin):
1148
1104
  )
1149
1105
  for dep in dep_set:
1150
1106
  if dep not in state_cls.vars and dep not in state_cls.backend_vars:
1151
- msg = f"ComputedVar {var._js_expr} on state {state.__name__} has an invalid dependency {state_name}.{dep}"
1107
+ msg = f"ComputedVar {var._name} on state {state.__name__} has an invalid dependency {state_name}.{dep}"
1152
1108
  raise exceptions.VarDependencyError(msg)
1153
1109
 
1154
1110
  for substate in state.class_subclasses:
1155
1111
  self._validate_var_dependencies(substate)
1156
1112
 
1157
- def _compile(self, export: bool = False, dry_run: bool = False):
1113
+ def _compile(self, prerender_routes: bool = False, dry_run: bool = False):
1158
1114
  """Compile the app and output it to the pages folder.
1159
1115
 
1160
1116
  Args:
1161
- export: Whether to compile the app for export.
1117
+ prerender_routes: Whether to prerender the routes.
1162
1118
  dry_run: Whether to compile the app without saving it.
1163
1119
 
1164
1120
  Raises:
@@ -1329,17 +1285,6 @@ class App(MiddlewareMixin, LifespanMixin):
1329
1285
  # Add the app wrappers from this component.
1330
1286
  app_wrappers.update(component._get_all_app_wrap_components())
1331
1287
 
1332
- if self.error_boundary:
1333
- from reflex.compiler.compiler import into_component
1334
-
1335
- console.deprecate(
1336
- feature_name="App.error_boundary",
1337
- reason="Use app_wraps instead.",
1338
- deprecation_version="0.7.1",
1339
- removal_version="0.8.0",
1340
- )
1341
- app_wrappers[(55, "ErrorBoundary")] = into_component(self.error_boundary)
1342
-
1343
1288
  # Perform auto-memoization of stateful components.
1344
1289
  with console.timing("Auto-memoize StatefulComponents"):
1345
1290
  (
@@ -1417,7 +1362,9 @@ class App(MiddlewareMixin, LifespanMixin):
1417
1362
  )
1418
1363
 
1419
1364
  # Compile the root stylesheet with base styles.
1420
- _submit_work(compiler.compile_root_stylesheet, self.stylesheets)
1365
+ _submit_work(
1366
+ compiler.compile_root_stylesheet, self.stylesheets, self.reset_style
1367
+ )
1421
1368
 
1422
1369
  # Compile the theme.
1423
1370
  _submit_work(compile_theme, self.style)
@@ -1441,6 +1388,7 @@ class App(MiddlewareMixin, LifespanMixin):
1441
1388
  )
1442
1389
  )
1443
1390
  ),
1391
+ unevaluated_pages=list(self._unevaluated_pages.values()),
1444
1392
  )
1445
1393
 
1446
1394
  # Wait for all compilation tasks to complete.
@@ -1484,15 +1432,9 @@ class App(MiddlewareMixin, LifespanMixin):
1484
1432
  with console.timing("Install Frontend Packages"):
1485
1433
  self._get_frontend_packages(all_imports)
1486
1434
 
1487
- # Setup the next.config.js
1488
- transpile_packages = [
1489
- package
1490
- for package, import_vars in all_imports.items()
1491
- if any(import_var.transpile for import_var in import_vars)
1492
- ]
1493
- prerequisites.update_next_config(
1494
- export=export,
1495
- transpile_packages=transpile_packages,
1435
+ # Setup the react-router.config.js
1436
+ prerequisites.update_react_router_config(
1437
+ prerender_routes=prerender_routes,
1496
1438
  )
1497
1439
 
1498
1440
  if is_prod_mode():
@@ -1501,9 +1443,11 @@ class App(MiddlewareMixin, LifespanMixin):
1501
1443
  else:
1502
1444
  # In dev mode, delete removed pages and update existing pages.
1503
1445
  keep_files = [Path(output_path) for output_path, _ in compile_results]
1504
- for p in Path(prerequisites.get_web_dir() / constants.Dirs.PAGES).rglob(
1505
- "*"
1506
- ):
1446
+ for p in Path(
1447
+ prerequisites.get_web_dir()
1448
+ / constants.Dirs.PAGES
1449
+ / constants.Dirs.ROUTES
1450
+ ).rglob("*"):
1507
1451
  if p.is_file() and p not in keep_files:
1508
1452
  # Remove pages that are no longer in the app.
1509
1453
  p.unlink()
@@ -1740,7 +1684,7 @@ class App(MiddlewareMixin, LifespanMixin):
1740
1684
 
1741
1685
  async def process(
1742
1686
  app: App, event: Event, sid: str, headers: dict, client_ip: str
1743
- ) -> AsyncIterator[StateUpdate]:
1687
+ ) -> AsyncGenerator[StateUpdate]:
1744
1688
  """Process an event.
1745
1689
 
1746
1690
  Args:
@@ -1792,6 +1736,11 @@ async def process(
1792
1736
  # assignment will recurse into substates and force recalculation of
1793
1737
  # dependent ComputedVar (dynamic route variables)
1794
1738
  state.router_data = router_data
1739
+ router_data[constants.RouteVar.PATH] = "/" + (
1740
+ app.router(path) or "404"
1741
+ if (path := router_data.get(constants.RouteVar.PATH))
1742
+ else "404"
1743
+ ).removeprefix("/")
1795
1744
  state.router = RouterData(router_data)
1796
1745
 
1797
1746
  # Preprocess the event.
@@ -1970,7 +1919,6 @@ def upload(app: App):
1970
1919
  UploadFile(
1971
1920
  file=content_copy,
1972
1921
  path=Path(file.filename.lstrip("/")) if file.filename else None,
1973
- _deprecated_filename=file.filename,
1974
1922
  size=file.size,
1975
1923
  headers=file.headers,
1976
1924
  )
@@ -2131,10 +2079,13 @@ class EventNamespace(AsyncNamespace):
2131
2079
  except (KeyError, IndexError):
2132
2080
  client_ip = environ.get("REMOTE_ADDR", "0.0.0.0")
2133
2081
 
2134
- # Process the events.
2135
- async for update in process(self.app, event, sid, headers, client_ip):
2136
- # Emit the update from processing the event.
2137
- await self.emit_update(update=update, sid=sid)
2082
+ async with contextlib.aclosing(
2083
+ process(self.app, event, sid, headers, client_ip)
2084
+ ) as updates_gen:
2085
+ # Process the events.
2086
+ async for update in updates_gen:
2087
+ # Emit the update from processing the event.
2088
+ await self.emit_update(update=update, sid=sid)
2138
2089
 
2139
2090
  async def on_ping(self, sid: str):
2140
2091
  """Event for testing the API endpoint.
reflex/base.py CHANGED
@@ -1,48 +1,6 @@
1
1
  """Define the base Reflex class."""
2
2
 
3
- from __future__ import annotations
4
-
5
- import os
6
- from typing import TYPE_CHECKING, Any
7
-
8
- import pydantic.v1.main as pydantic_main
9
3
  from pydantic.v1 import BaseModel
10
- from pydantic.v1.fields import ModelField
11
-
12
-
13
- def validate_field_name(bases: list[type[BaseModel]], field_name: str) -> None:
14
- """Ensure that the field's name does not shadow an existing attribute of the model.
15
-
16
- Args:
17
- bases: List of base models to check for shadowed attrs.
18
- field_name: name of attribute
19
-
20
- Raises:
21
- VarNameError: If state var field shadows another in its parent state
22
- """
23
- from reflex.utils.exceptions import VarNameError
24
-
25
- # can't use reflex.config.environment here cause of circular import
26
- reload = os.getenv("__RELOAD_CONFIG", "").lower() == "true"
27
- base = None
28
- try:
29
- for base in bases:
30
- if not reload and getattr(base, field_name, None):
31
- pass
32
- except TypeError as te:
33
- msg = (
34
- f'State var "{field_name}" in {base} has been shadowed by a substate var; '
35
- f'use a different field name instead".'
36
- )
37
- raise VarNameError(msg) from te
38
-
39
-
40
- # monkeypatch pydantic validate_field_name method to skip validating
41
- # shadowed state vars when reloading app via utils.prerequisites.get_app(reload=True)
42
- pydantic_main.validate_field_name = validate_field_name # pyright: ignore [reportPrivateImportUsage]
43
-
44
- if TYPE_CHECKING:
45
- from reflex.vars import Var
46
4
 
47
5
 
48
6
  class Base(BaseModel):
@@ -75,7 +33,7 @@ class Base(BaseModel):
75
33
  default=serialize,
76
34
  )
77
35
 
78
- def set(self, **kwargs: Any):
36
+ def set(self, **kwargs: object):
79
37
  """Set multiple fields and return the object.
80
38
 
81
39
  Args:
@@ -87,47 +45,3 @@ class Base(BaseModel):
87
45
  for key, value in kwargs.items():
88
46
  setattr(self, key, value)
89
47
  return self
90
-
91
- @classmethod
92
- def get_fields(cls) -> dict[str, ModelField]:
93
- """Get the fields of the object.
94
-
95
- Returns:
96
- The fields of the object.
97
- """
98
- return cls.__fields__
99
-
100
- @classmethod
101
- def add_field(cls, var: Var, default_value: Any):
102
- """Add a pydantic field after class definition.
103
-
104
- Used by State.add_var() to correctly handle the new variable.
105
-
106
- Args:
107
- var: The variable to add a pydantic field for.
108
- default_value: The default value of the field
109
- """
110
- var_name = var._var_field_name
111
- new_field = ModelField.infer(
112
- name=var_name,
113
- value=default_value,
114
- annotation=var._var_type,
115
- class_validators=None,
116
- config=cls.__config__,
117
- )
118
- cls.__fields__.update({var_name: new_field})
119
-
120
- def get_value(self, key: str) -> Any:
121
- """Get the value of a field.
122
-
123
- Args:
124
- key: The key of the field.
125
-
126
- Returns:
127
- The value of the field.
128
- """
129
- if isinstance(key, str):
130
- # Seems like this function signature was wrong all along?
131
- # If the user wants a field that we know of, get it and pass it off to _get_value
132
- return getattr(self, key)
133
- return key