reflex 0.7.14a6__py3-none-any.whl → 0.8.0a2__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 (211) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +16 -10
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
  7. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -8
  8. reflex/.templates/web/app/entry.client.js +8 -0
  9. reflex/.templates/web/app/routes.js +10 -0
  10. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -37
  11. reflex/.templates/web/postcss.config.js +1 -1
  12. reflex/.templates/web/react-router.config.js +6 -0
  13. reflex/.templates/web/utils/client_side_routing.js +21 -19
  14. reflex/.templates/web/utils/react-theme.js +92 -0
  15. reflex/.templates/web/utils/state.js +160 -67
  16. reflex/.templates/web/vite.config.js +32 -0
  17. reflex/__init__.py +1 -6
  18. reflex/__init__.pyi +0 -4
  19. reflex/app.py +53 -116
  20. reflex/base.py +1 -87
  21. reflex/compiler/compiler.py +41 -8
  22. reflex/compiler/templates.py +3 -3
  23. reflex/compiler/utils.py +73 -33
  24. reflex/components/__init__.py +0 -2
  25. reflex/components/__init__.pyi +0 -3
  26. reflex/components/base/__init__.py +1 -5
  27. reflex/components/base/__init__.pyi +4 -6
  28. reflex/components/base/app_wrap.pyi +5 -4
  29. reflex/components/base/body.pyi +5 -4
  30. reflex/components/base/document.py +18 -14
  31. reflex/components/base/document.pyi +83 -27
  32. reflex/components/base/error_boundary.pyi +5 -4
  33. reflex/components/base/fragment.pyi +5 -4
  34. reflex/components/base/link.pyi +9 -7
  35. reflex/components/base/meta.pyi +17 -13
  36. reflex/components/base/script.py +60 -58
  37. reflex/components/base/script.pyi +246 -31
  38. reflex/components/base/strict_mode.pyi +5 -4
  39. reflex/components/component.py +146 -217
  40. reflex/components/core/__init__.py +1 -0
  41. reflex/components/core/__init__.pyi +1 -0
  42. reflex/components/core/auto_scroll.pyi +5 -4
  43. reflex/components/core/banner.pyi +25 -19
  44. reflex/components/core/client_side_routing.py +7 -6
  45. reflex/components/core/client_side_routing.pyi +6 -56
  46. reflex/components/core/clipboard.pyi +5 -4
  47. reflex/components/core/debounce.py +1 -0
  48. reflex/components/core/debounce.pyi +5 -4
  49. reflex/components/core/foreach.py +3 -2
  50. reflex/components/core/helmet.py +14 -0
  51. reflex/components/{next/base.pyi → core/helmet.pyi} +10 -7
  52. reflex/components/core/html.pyi +5 -4
  53. reflex/components/core/sticky.pyi +17 -13
  54. reflex/components/core/upload.py +2 -1
  55. reflex/components/core/upload.pyi +21 -16
  56. reflex/components/datadisplay/code.py +2 -72
  57. reflex/components/datadisplay/code.pyi +9 -10
  58. reflex/components/datadisplay/dataeditor.pyi +11 -6
  59. reflex/components/datadisplay/shiki_code_block.pyi +13 -10
  60. reflex/components/dynamic.py +5 -5
  61. reflex/components/el/element.pyi +5 -4
  62. reflex/components/el/elements/base.pyi +5 -4
  63. reflex/components/el/elements/forms.pyi +69 -52
  64. reflex/components/el/elements/inline.pyi +113 -85
  65. reflex/components/el/elements/media.pyi +105 -79
  66. reflex/components/el/elements/metadata.pyi +25 -19
  67. reflex/components/el/elements/other.pyi +29 -22
  68. reflex/components/el/elements/scripts.pyi +13 -10
  69. reflex/components/el/elements/sectioning.pyi +61 -46
  70. reflex/components/el/elements/tables.pyi +41 -31
  71. reflex/components/el/elements/typography.pyi +61 -46
  72. reflex/components/field.py +175 -0
  73. reflex/components/gridjs/datatable.py +2 -2
  74. reflex/components/gridjs/datatable.pyi +11 -9
  75. reflex/components/lucide/icon.py +6 -2
  76. reflex/components/lucide/icon.pyi +15 -10
  77. reflex/components/markdown/markdown.pyi +5 -4
  78. reflex/components/moment/moment.pyi +5 -4
  79. reflex/components/plotly/plotly.pyi +19 -10
  80. reflex/components/props.py +376 -27
  81. reflex/components/radix/primitives/accordion.py +8 -1
  82. reflex/components/radix/primitives/accordion.pyi +29 -22
  83. reflex/components/radix/primitives/base.pyi +9 -7
  84. reflex/components/radix/primitives/drawer.pyi +45 -34
  85. reflex/components/radix/primitives/form.pyi +41 -31
  86. reflex/components/radix/primitives/progress.pyi +21 -16
  87. reflex/components/radix/primitives/slider.pyi +21 -16
  88. reflex/components/radix/themes/base.py +3 -3
  89. reflex/components/radix/themes/base.pyi +33 -25
  90. reflex/components/radix/themes/color_mode.pyi +13 -10
  91. reflex/components/radix/themes/components/alert_dialog.pyi +29 -22
  92. reflex/components/radix/themes/components/aspect_ratio.pyi +5 -4
  93. reflex/components/radix/themes/components/avatar.pyi +5 -4
  94. reflex/components/radix/themes/components/badge.pyi +5 -4
  95. reflex/components/radix/themes/components/button.pyi +5 -4
  96. reflex/components/radix/themes/components/callout.pyi +21 -16
  97. reflex/components/radix/themes/components/card.pyi +5 -4
  98. reflex/components/radix/themes/components/checkbox.pyi +13 -10
  99. reflex/components/radix/themes/components/checkbox_cards.pyi +9 -7
  100. reflex/components/radix/themes/components/checkbox_group.pyi +9 -7
  101. reflex/components/radix/themes/components/context_menu.pyi +53 -40
  102. reflex/components/radix/themes/components/data_list.pyi +17 -13
  103. reflex/components/radix/themes/components/dialog.pyi +29 -22
  104. reflex/components/radix/themes/components/dropdown_menu.pyi +33 -25
  105. reflex/components/radix/themes/components/hover_card.pyi +17 -13
  106. reflex/components/radix/themes/components/icon_button.pyi +5 -4
  107. reflex/components/radix/themes/components/inset.pyi +5 -4
  108. reflex/components/radix/themes/components/popover.pyi +17 -13
  109. reflex/components/radix/themes/components/progress.pyi +5 -4
  110. reflex/components/radix/themes/components/radio.pyi +5 -4
  111. reflex/components/radix/themes/components/radio_cards.pyi +9 -7
  112. reflex/components/radix/themes/components/radio_group.pyi +17 -13
  113. reflex/components/radix/themes/components/scroll_area.pyi +5 -4
  114. reflex/components/radix/themes/components/segmented_control.pyi +9 -7
  115. reflex/components/radix/themes/components/select.pyi +37 -28
  116. reflex/components/radix/themes/components/separator.pyi +5 -4
  117. reflex/components/radix/themes/components/skeleton.pyi +5 -4
  118. reflex/components/radix/themes/components/slider.pyi +5 -4
  119. reflex/components/radix/themes/components/spinner.pyi +5 -4
  120. reflex/components/radix/themes/components/switch.pyi +5 -4
  121. reflex/components/radix/themes/components/table.pyi +29 -22
  122. reflex/components/radix/themes/components/tabs.pyi +21 -16
  123. reflex/components/radix/themes/components/text_area.pyi +5 -4
  124. reflex/components/radix/themes/components/text_field.pyi +13 -10
  125. reflex/components/radix/themes/components/tooltip.pyi +5 -4
  126. reflex/components/radix/themes/layout/base.pyi +5 -4
  127. reflex/components/radix/themes/layout/box.pyi +5 -4
  128. reflex/components/radix/themes/layout/center.pyi +5 -4
  129. reflex/components/radix/themes/layout/container.pyi +5 -4
  130. reflex/components/radix/themes/layout/flex.pyi +5 -4
  131. reflex/components/radix/themes/layout/grid.pyi +5 -4
  132. reflex/components/radix/themes/layout/list.pyi +21 -16
  133. reflex/components/radix/themes/layout/section.pyi +5 -4
  134. reflex/components/radix/themes/layout/spacer.pyi +5 -4
  135. reflex/components/radix/themes/layout/stack.pyi +13 -10
  136. reflex/components/radix/themes/typography/blockquote.pyi +5 -4
  137. reflex/components/radix/themes/typography/code.pyi +5 -4
  138. reflex/components/radix/themes/typography/heading.pyi +5 -4
  139. reflex/components/radix/themes/typography/link.py +46 -11
  140. reflex/components/radix/themes/typography/link.pyi +311 -6
  141. reflex/components/radix/themes/typography/text.pyi +29 -22
  142. reflex/components/react_player/audio.pyi +5 -4
  143. reflex/components/react_player/react_player.pyi +5 -4
  144. reflex/components/react_player/video.pyi +5 -4
  145. reflex/components/recharts/cartesian.py +2 -1
  146. reflex/components/recharts/cartesian.pyi +65 -46
  147. reflex/components/recharts/charts.py +4 -2
  148. reflex/components/recharts/charts.pyi +36 -24
  149. reflex/components/recharts/general.pyi +24 -18
  150. reflex/components/recharts/polar.py +8 -4
  151. reflex/components/recharts/polar.pyi +16 -10
  152. reflex/components/recharts/recharts.pyi +9 -7
  153. reflex/components/sonner/toast.py +2 -2
  154. reflex/components/sonner/toast.pyi +10 -8
  155. reflex/config.py +3 -77
  156. reflex/constants/__init__.py +2 -2
  157. reflex/constants/base.py +28 -11
  158. reflex/constants/compiler.py +5 -3
  159. reflex/constants/event.py +1 -0
  160. reflex/constants/installer.py +22 -16
  161. reflex/constants/route.py +19 -7
  162. reflex/constants/state.py +2 -0
  163. reflex/custom_components/custom_components.py +0 -14
  164. reflex/environment.py +1 -1
  165. reflex/event.py +178 -81
  166. reflex/experimental/__init__.py +0 -30
  167. reflex/istate/proxy.py +5 -3
  168. reflex/page.py +0 -27
  169. reflex/plugins/__init__.py +3 -2
  170. reflex/plugins/base.py +5 -1
  171. reflex/plugins/shared_tailwind.py +158 -0
  172. reflex/plugins/sitemap.py +206 -0
  173. reflex/plugins/tailwind_v3.py +13 -106
  174. reflex/plugins/tailwind_v4.py +15 -108
  175. reflex/reflex.py +1 -0
  176. reflex/route.py +15 -21
  177. reflex/state.py +134 -140
  178. reflex/testing.py +58 -10
  179. reflex/utils/build.py +38 -82
  180. reflex/utils/exec.py +59 -161
  181. reflex/utils/export.py +2 -2
  182. reflex/utils/imports.py +0 -4
  183. reflex/utils/misc.py +28 -0
  184. reflex/utils/prerequisites.py +65 -62
  185. reflex/utils/processes.py +8 -7
  186. reflex/utils/pyi_generator.py +21 -9
  187. reflex/utils/serializers.py +14 -1
  188. reflex/utils/types.py +196 -61
  189. reflex/vars/__init__.py +2 -0
  190. reflex/vars/base.py +367 -134
  191. {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/METADATA +12 -5
  192. {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/RECORD +195 -202
  193. reflex/.templates/web/next.config.js +0 -7
  194. reflex/components/base/head.py +0 -20
  195. reflex/components/base/head.pyi +0 -116
  196. reflex/components/next/__init__.py +0 -10
  197. reflex/components/next/base.py +0 -7
  198. reflex/components/next/image.py +0 -117
  199. reflex/components/next/image.pyi +0 -94
  200. reflex/components/next/link.py +0 -20
  201. reflex/components/next/link.pyi +0 -67
  202. reflex/components/next/video.py +0 -38
  203. reflex/components/next/video.pyi +0 -68
  204. reflex/components/suneditor/__init__.py +0 -5
  205. reflex/components/suneditor/editor.py +0 -269
  206. reflex/components/suneditor/editor.pyi +0 -199
  207. reflex/experimental/layout.py +0 -254
  208. reflex/experimental/layout.pyi +0 -814
  209. {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/WHEEL +0 -0
  210. {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/entry_points.txt +0 -0
  211. {reflex-0.7.14a6.dist-info → reflex-0.8.0a2.dist-info}/licenses/LICENSE +0 -0
reflex/app.py CHANGED
@@ -13,7 +13,7 @@ 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 AsyncIterator, Callable, Coroutine, Mapping, Sequence
17
17
  from datetime import datetime
18
18
  from pathlib import Path
19
19
  from timeit import default_timer as timer
@@ -32,7 +32,6 @@ from starlette.middleware import cors
32
32
  from starlette.requests import ClientDisconnect, Request
33
33
  from starlette.responses import JSONResponse, Response, StreamingResponse
34
34
  from starlette.staticfiles import StaticFiles
35
- from typing_extensions import deprecated
36
35
 
37
36
  from reflex import constants
38
37
  from reflex.admin import AdminDash
@@ -61,7 +60,7 @@ from reflex.components.core.banner import (
61
60
  )
62
61
  from reflex.components.core.breakpoints import set_breakpoints
63
62
  from reflex.components.core.client_side_routing import (
64
- Default404Page,
63
+ default_404_page,
65
64
  wait_for_client_redirect,
66
65
  )
67
66
  from reflex.components.core.sticky import sticky
@@ -246,8 +245,6 @@ class UploadFile(StarletteUploadFile):
246
245
 
247
246
  path: Path | None = dataclasses.field(default=None)
248
247
 
249
- _deprecated_filename: str | None = dataclasses.field(default=None)
250
-
251
248
  size: int | None = dataclasses.field(default=None)
252
249
 
253
250
  headers: Headers = dataclasses.field(default_factory=Headers)
@@ -263,21 +260,6 @@ class UploadFile(StarletteUploadFile):
263
260
  return self.path.name
264
261
  return None
265
262
 
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
263
 
282
264
  @dataclasses.dataclass(
283
265
  frozen=True,
@@ -291,8 +273,8 @@ class UnevaluatedPage:
291
273
  description: Var | str | None
292
274
  image: str
293
275
  on_load: EventType[()] | None
294
- meta: list[dict[str, str]]
295
- context: dict[str, Any] | None
276
+ meta: Sequence[Mapping[str, str]]
277
+ context: Mapping[str, Any]
296
278
 
297
279
  def merged_with(self, other: UnevaluatedPage) -> UnevaluatedPage:
298
280
  """Merge the other page into this one.
@@ -355,9 +337,6 @@ class App(MiddlewareMixin, LifespanMixin):
355
337
  default=None
356
338
  )
357
339
 
358
- # Error boundary component to wrap the app with.
359
- error_boundary: ComponentCallable | None = dataclasses.field(default=None)
360
-
361
340
  # 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
341
  app_wraps: dict[tuple[int, str], Callable[[bool], Component | None]] = (
363
342
  dataclasses.field(
@@ -443,24 +422,6 @@ class App(MiddlewareMixin, LifespanMixin):
443
422
  # FastAPI app for compatibility with FastAPI.
444
423
  _cached_fastapi_app: FastAPI | None = None
445
424
 
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
425
  @property
465
426
  def event_namespace(self) -> EventNamespace | None:
466
427
  """Get the event namespace.
@@ -619,7 +580,7 @@ class App(MiddlewareMixin, LifespanMixin):
619
580
  self._apply_decorated_pages()
620
581
 
621
582
  compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit(
622
- self._compile
583
+ self._compile, prerender_routes=is_prod_mode()
623
584
  )
624
585
 
625
586
  def callback(f: concurrent.futures.Future):
@@ -800,7 +761,7 @@ class App(MiddlewareMixin, LifespanMixin):
800
761
 
801
762
  if route == constants.Page404.SLUG:
802
763
  if component is None:
803
- component = Default404Page.create()
764
+ component = default_404_page
804
765
  component = wait_for_client_redirect(self._generate_component(component))
805
766
  title = title or constants.Page404.TITLE
806
767
  description = description or constants.Page404.DESCRIPTION
@@ -821,7 +782,7 @@ class App(MiddlewareMixin, LifespanMixin):
821
782
  image=image,
822
783
  on_load=on_load,
823
784
  meta=meta,
824
- context=context,
785
+ context=context or {},
825
786
  )
826
787
 
827
788
  if route in self._unevaluated_pages:
@@ -892,15 +853,44 @@ class App(MiddlewareMixin, LifespanMixin):
892
853
  Returns:
893
854
  The load events for the route.
894
855
  """
895
- route = route.lstrip("/")
856
+ route = route.lstrip("/").rstrip("/")
896
857
  if route == "":
897
- route = constants.PageNames.INDEX_ROUTE
898
- return self._load_events.get(route, [])
858
+ return self._load_events.get(constants.PageNames.INDEX_ROUTE, [])
859
+
860
+ # Separate the pages by route type.
861
+ static_page_paths_to_page_route = {}
862
+ dynamic_page_paths_to_page_route = {}
863
+ for page_route in list(self._pages) + list(self._unevaluated_pages):
864
+ page_path = page_route.lstrip("/").rstrip("/")
865
+ if "[" in page_path and "]" in page_path:
866
+ dynamic_page_paths_to_page_route[page_path] = page_route
867
+ else:
868
+ static_page_paths_to_page_route[page_path] = page_route
869
+
870
+ # Check for static routes.
871
+ if (page_route := static_page_paths_to_page_route.get(route)) is not None:
872
+ return self._load_events.get(page_route, [])
873
+
874
+ # Check for dynamic routes.
875
+ parts = route.split("/")
876
+ for page_path, page_route in dynamic_page_paths_to_page_route.items():
877
+ page_parts = page_path.split("/")
878
+ if len(page_parts) != len(parts):
879
+ continue
880
+ if all(
881
+ part == page_part
882
+ or (page_part.startswith("[") and page_part.endswith("]"))
883
+ for part, page_part in zip(parts, page_parts, strict=False)
884
+ ):
885
+ return self._load_events.get(page_route, [])
886
+
887
+ # Default to 404 page load events if no match found.
888
+ return self._load_events.get("404", [])
899
889
 
900
890
  def _check_routes_conflict(self, new_route: str):
901
891
  """Verify if there is any conflict between the new route and any existing route.
902
892
 
903
- Based on conflicts that NextJS would throw if not intercepted.
893
+ Based on conflicts that React Router would throw if not intercepted.
904
894
 
905
895
  Raises:
906
896
  RouteValueError: exception showing which conflict exist with the route to be added
@@ -938,44 +928,6 @@ class App(MiddlewareMixin, LifespanMixin):
938
928
  # info1 will break away into its own tree.
939
929
  break
940
930
 
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
931
  def _setup_admin_dash(self):
980
932
  """Setup the admin dash."""
981
933
  try:
@@ -1024,7 +976,7 @@ class App(MiddlewareMixin, LifespanMixin):
1024
976
  for i, tags in imports.items()
1025
977
  if i not in dependencies
1026
978
  and i not in dev_dependencies
1027
- and not any(i.startswith(prefix) for prefix in ["/", "$/", ".", "next/"])
979
+ and not any(i.startswith(prefix) for prefix in ["/", "$/", "."])
1028
980
  and i != ""
1029
981
  and any(tag.install for tag in tags)
1030
982
  }
@@ -1148,17 +1100,17 @@ class App(MiddlewareMixin, LifespanMixin):
1148
1100
  )
1149
1101
  for dep in dep_set:
1150
1102
  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}"
1103
+ msg = f"ComputedVar {var._name} on state {state.__name__} has an invalid dependency {state_name}.{dep}"
1152
1104
  raise exceptions.VarDependencyError(msg)
1153
1105
 
1154
1106
  for substate in state.class_subclasses:
1155
1107
  self._validate_var_dependencies(substate)
1156
1108
 
1157
- def _compile(self, export: bool = False, dry_run: bool = False):
1109
+ def _compile(self, prerender_routes: bool = False, dry_run: bool = False):
1158
1110
  """Compile the app and output it to the pages folder.
1159
1111
 
1160
1112
  Args:
1161
- export: Whether to compile the app for export.
1113
+ prerender_routes: Whether to prerender the routes.
1162
1114
  dry_run: Whether to compile the app without saving it.
1163
1115
 
1164
1116
  Raises:
@@ -1329,17 +1281,6 @@ class App(MiddlewareMixin, LifespanMixin):
1329
1281
  # Add the app wrappers from this component.
1330
1282
  app_wrappers.update(component._get_all_app_wrap_components())
1331
1283
 
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
1284
  # Perform auto-memoization of stateful components.
1344
1285
  with console.timing("Auto-memoize StatefulComponents"):
1345
1286
  (
@@ -1441,6 +1382,7 @@ class App(MiddlewareMixin, LifespanMixin):
1441
1382
  )
1442
1383
  )
1443
1384
  ),
1385
+ unevaluated_pages=list(self._unevaluated_pages.values()),
1444
1386
  )
1445
1387
 
1446
1388
  # Wait for all compilation tasks to complete.
@@ -1484,15 +1426,9 @@ class App(MiddlewareMixin, LifespanMixin):
1484
1426
  with console.timing("Install Frontend Packages"):
1485
1427
  self._get_frontend_packages(all_imports)
1486
1428
 
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,
1429
+ # Setup the react-router.config.js
1430
+ prerequisites.update_react_router_config(
1431
+ prerender_routes=prerender_routes,
1496
1432
  )
1497
1433
 
1498
1434
  if is_prod_mode():
@@ -1501,9 +1437,11 @@ class App(MiddlewareMixin, LifespanMixin):
1501
1437
  else:
1502
1438
  # In dev mode, delete removed pages and update existing pages.
1503
1439
  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
- ):
1440
+ for p in Path(
1441
+ prerequisites.get_web_dir()
1442
+ / constants.Dirs.PAGES
1443
+ / constants.Dirs.ROUTES
1444
+ ).rglob("*"):
1507
1445
  if p.is_file() and p not in keep_files:
1508
1446
  # Remove pages that are no longer in the app.
1509
1447
  p.unlink()
@@ -1970,7 +1908,6 @@ def upload(app: App):
1970
1908
  UploadFile(
1971
1909
  file=content_copy,
1972
1910
  path=Path(file.filename.lstrip("/")) if file.filename else None,
1973
- _deprecated_filename=file.filename,
1974
1911
  size=file.size,
1975
1912
  headers=file.headers,
1976
1913
  )
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
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  import sys
6
6
  from collections.abc import Iterable, Sequence
7
- from datetime import datetime
8
7
  from inspect import getmodule
9
8
  from pathlib import Path
10
9
  from typing import TYPE_CHECKING
@@ -21,12 +20,14 @@ from reflex.components.component import (
21
20
  )
22
21
  from reflex.config import get_config
23
22
  from reflex.constants.compiler import PageNames
23
+ from reflex.constants.state import FIELD_MARKER
24
24
  from reflex.environment import environment
25
25
  from reflex.state import BaseState
26
26
  from reflex.style import SYSTEM_COLOR_MODE
27
27
  from reflex.utils import console, path_ops
28
28
  from reflex.utils.exceptions import ReflexError
29
29
  from reflex.utils.exec import is_prod_mode
30
+ from reflex.utils.format import to_title_case
30
31
  from reflex.utils.imports import ImportVar
31
32
  from reflex.utils.prerequisites import get_web_dir
32
33
  from reflex.vars.base import LiteralVar, Var
@@ -124,21 +125,18 @@ def _compile_contexts(state: type[BaseState] | None, theme: Component | None) ->
124
125
  if appearance is None or str(LiteralVar.create(appearance)) == '"inherit"':
125
126
  appearance = LiteralVar.create(SYSTEM_COLOR_MODE)
126
127
 
127
- last_compiled_time = str(datetime.now())
128
128
  return (
129
129
  templates.CONTEXT.render(
130
130
  initial_state=utils.compile_state(state),
131
131
  state_name=state.get_name(),
132
132
  client_storage=utils.compile_client_storage(state),
133
133
  is_dev_mode=not is_prod_mode(),
134
- last_compiled_time=last_compiled_time,
135
134
  default_color_mode=appearance,
136
135
  )
137
136
  if state
138
137
  else templates.CONTEXT.render(
139
138
  is_dev_mode=not is_prod_mode(),
140
139
  default_color_mode=appearance,
141
- last_compiled_time=last_compiled_time,
142
140
  )
143
141
  )
144
142
 
@@ -427,7 +425,7 @@ def _compile_stateful_components(
427
425
 
428
426
  # Include custom code in the shared component.
429
427
  rendered_components.update(
430
- dict.fromkeys(component._get_all_custom_code()),
428
+ dict.fromkeys(component._get_all_custom_code(export=True)),
431
429
  )
432
430
 
433
431
  # Include all imports in the shared component.
@@ -469,7 +467,9 @@ def compile_document_root(
469
467
  The path and code of the compiled document root.
470
468
  """
471
469
  # Get the path for the output file.
472
- output_path = utils.get_page_path(constants.PageNames.DOCUMENT_ROOT)
470
+ output_path = str(
471
+ get_web_dir() / constants.Dirs.PAGES / constants.PageNames.DOCUMENT_ROOT
472
+ )
473
473
 
474
474
  # Create the document root.
475
475
  document_root = utils.create_document_root(
@@ -491,7 +491,9 @@ def compile_app(app_root: Component) -> tuple[str, str]:
491
491
  The path and code of the compiled app wrapper.
492
492
  """
493
493
  # Get the path for the output file.
494
- output_path = utils.get_page_path(constants.PageNames.APP_ROOT)
494
+ output_path = str(
495
+ get_web_dir() / constants.Dirs.PAGES / constants.PageNames.APP_ROOT
496
+ )
495
497
 
496
498
  # Compile the document root.
497
499
  code = _compile_app(app_root)
@@ -606,7 +608,7 @@ def purge_web_pages_dir():
606
608
  return
607
609
 
608
610
  # Empty out the web pages directory.
609
- utils.empty_dir(get_web_dir() / constants.Dirs.PAGES, keep_files=["_app.js"])
611
+ utils.empty_dir(get_web_dir() / constants.Dirs.PAGES, keep_files=["routes.js"])
610
612
 
611
613
 
612
614
  if TYPE_CHECKING:
@@ -668,6 +670,32 @@ def readable_name_from_component(
668
670
  return None
669
671
 
670
672
 
673
+ def _modify_exception(e: Exception) -> None:
674
+ """Modify the exception to make it more readable.
675
+
676
+ Args:
677
+ e: The exception to modify.
678
+ """
679
+ if len(e.args) == 1 and isinstance((msg := e.args[0]), str):
680
+ while (state_index := msg.find("reflex___")) != -1:
681
+ dot_index = msg.find(".", state_index)
682
+ if dot_index == -1:
683
+ break
684
+ state_name = msg[state_index:dot_index]
685
+ module_dot_state_name = state_name.replace("___", ".").rsplit("__", 1)[-1]
686
+ module_path, _, state_snake_case = module_dot_state_name.rpartition(".")
687
+ if not state_snake_case:
688
+ break
689
+ actual_state_name = to_title_case(state_snake_case)
690
+ msg = (
691
+ f"{msg[:state_index]}{module_path}.{actual_state_name}{msg[dot_index:]}"
692
+ )
693
+
694
+ msg = msg.replace(FIELD_MARKER, "")
695
+
696
+ e.args = (msg,)
697
+
698
+
671
699
  def into_component(component: Component | ComponentCallable) -> Component:
672
700
  """Convert a component to a Component.
673
701
 
@@ -692,6 +720,7 @@ def into_component(component: Component | ComponentCallable) -> Component:
692
720
  component_called = component()
693
721
  except KeyError as e:
694
722
  if isinstance(e, ReflexError):
723
+ _modify_exception(e)
695
724
  raise
696
725
  key = e.args[0] if e.args else None
697
726
  if key is not None and isinstance(key, Var):
@@ -701,6 +730,7 @@ def into_component(component: Component | ComponentCallable) -> Component:
701
730
  raise
702
731
  except TypeError as e:
703
732
  if isinstance(e, ReflexError):
733
+ _modify_exception(e)
704
734
  raise
705
735
  message = e.args[0] if e.args else None
706
736
  if message and isinstance(message, str):
@@ -726,6 +756,9 @@ def into_component(component: Component | ComponentCallable) -> Component:
726
756
  "Cannot pass a Var to a built-in function. Consider moving the operation to the backend, using existing Var operations, or defining a custom Var operation."
727
757
  ).with_traceback(e.__traceback__) from None
728
758
  raise
759
+ except ReflexError as e:
760
+ _modify_exception(e)
761
+ raise
729
762
 
730
763
  if (converted := _into_component_once(component_called)) is not None:
731
764
  return converted
@@ -113,10 +113,10 @@ def from_string(source: str) -> Template:
113
113
  # Template for the Reflex config file.
114
114
  RXCONFIG = get_template("app/rxconfig.py.jinja2")
115
115
 
116
- # Code to render a NextJS Document root.
116
+ # Code to render the Document root.
117
117
  DOCUMENT_ROOT = get_template("web/pages/_document.js.jinja2")
118
118
 
119
- # Code to render NextJS App root.
119
+ # Code to render App root.
120
120
  APP_ROOT = get_template("web/pages/_app.js.jinja2")
121
121
 
122
122
  # Template for the theme file.
@@ -128,7 +128,7 @@ CONTEXT = get_template("web/utils/context.js.jinja2")
128
128
  # Template to render a component tag.
129
129
  COMPONENT = get_template("web/pages/component.js.jinja2")
130
130
 
131
- # Code to render a single NextJS page.
131
+ # Code to render a single react page.
132
132
  PAGE = get_template("web/pages/index.js.jinja2")
133
133
 
134
134
  # Code to render the custom components page.