reflex 0.7.14a5__py3-none-any.whl → 0.8.0a1__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.
- reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
- reflex/.templates/jinja/web/package.json.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +16 -10
- reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
- reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -6
- reflex/.templates/web/app/entry.client.js +8 -0
- reflex/.templates/web/app/routes.js +10 -0
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -32
- reflex/.templates/web/postcss.config.js +1 -1
- reflex/.templates/web/react-router.config.js +6 -0
- reflex/.templates/web/utils/client_side_routing.js +21 -19
- reflex/.templates/web/utils/react-theme.js +89 -0
- reflex/.templates/web/utils/state.js +155 -67
- reflex/.templates/web/vite.config.js +32 -0
- reflex/__init__.py +1 -6
- reflex/__init__.pyi +0 -4
- reflex/app.py +69 -132
- reflex/base.py +1 -87
- reflex/compiler/compiler.py +40 -3
- reflex/compiler/utils.py +54 -28
- reflex/components/__init__.py +0 -2
- reflex/components/__init__.pyi +0 -3
- reflex/components/base/__init__.py +1 -5
- reflex/components/base/__init__.pyi +4 -6
- reflex/components/base/app_wrap.pyi +5 -4
- reflex/components/base/body.pyi +5 -4
- reflex/components/base/document.py +18 -14
- reflex/components/base/document.pyi +83 -27
- reflex/components/base/error_boundary.pyi +5 -4
- reflex/components/base/fragment.pyi +5 -4
- reflex/components/base/link.pyi +9 -7
- reflex/components/base/meta.pyi +17 -13
- reflex/components/base/script.py +60 -58
- reflex/components/base/script.pyi +246 -31
- reflex/components/base/strict_mode.pyi +5 -4
- reflex/components/component.py +109 -194
- reflex/components/core/__init__.py +1 -0
- reflex/components/core/__init__.pyi +1 -0
- reflex/components/core/auto_scroll.pyi +5 -4
- reflex/components/core/banner.pyi +25 -19
- reflex/components/core/client_side_routing.py +7 -6
- reflex/components/core/client_side_routing.pyi +6 -56
- reflex/components/core/clipboard.pyi +5 -4
- reflex/components/core/debounce.py +1 -0
- reflex/components/core/debounce.pyi +5 -4
- reflex/components/core/foreach.py +3 -2
- reflex/components/core/helmet.py +14 -0
- reflex/components/{next/base.pyi → core/helmet.pyi} +10 -7
- reflex/components/core/html.pyi +5 -4
- reflex/components/core/sticky.pyi +17 -13
- reflex/components/core/upload.py +2 -1
- reflex/components/core/upload.pyi +21 -16
- reflex/components/datadisplay/code.pyi +9 -7
- reflex/components/datadisplay/dataeditor.pyi +5 -4
- reflex/components/datadisplay/shiki_code_block.pyi +13 -10
- reflex/components/dynamic.py +4 -5
- reflex/components/el/element.pyi +5 -4
- reflex/components/el/elements/base.pyi +5 -4
- reflex/components/el/elements/forms.pyi +69 -52
- reflex/components/el/elements/inline.pyi +113 -85
- reflex/components/el/elements/media.pyi +105 -79
- reflex/components/el/elements/metadata.pyi +25 -19
- reflex/components/el/elements/other.pyi +29 -22
- reflex/components/el/elements/scripts.pyi +13 -10
- reflex/components/el/elements/sectioning.pyi +61 -46
- reflex/components/el/elements/tables.pyi +41 -31
- reflex/components/el/elements/typography.pyi +61 -46
- reflex/components/field.py +175 -0
- reflex/components/gridjs/datatable.py +2 -2
- reflex/components/gridjs/datatable.pyi +11 -9
- reflex/components/lucide/icon.py +6 -2
- reflex/components/lucide/icon.pyi +15 -10
- reflex/components/markdown/markdown.pyi +5 -4
- reflex/components/moment/moment.pyi +5 -4
- reflex/components/plotly/plotly.pyi +19 -10
- reflex/components/props.py +376 -27
- reflex/components/radix/primitives/accordion.py +8 -1
- reflex/components/radix/primitives/accordion.pyi +29 -22
- reflex/components/radix/primitives/base.pyi +9 -7
- reflex/components/radix/primitives/drawer.pyi +45 -34
- reflex/components/radix/primitives/form.pyi +41 -31
- reflex/components/radix/primitives/progress.pyi +21 -16
- reflex/components/radix/primitives/slider.pyi +21 -16
- reflex/components/radix/themes/base.py +3 -3
- reflex/components/radix/themes/base.pyi +33 -25
- reflex/components/radix/themes/color_mode.pyi +13 -10
- reflex/components/radix/themes/components/alert_dialog.pyi +29 -22
- reflex/components/radix/themes/components/aspect_ratio.pyi +5 -4
- reflex/components/radix/themes/components/avatar.pyi +5 -4
- reflex/components/radix/themes/components/badge.pyi +5 -4
- reflex/components/radix/themes/components/button.pyi +5 -4
- reflex/components/radix/themes/components/callout.pyi +21 -16
- reflex/components/radix/themes/components/card.pyi +5 -4
- reflex/components/radix/themes/components/checkbox.pyi +13 -10
- reflex/components/radix/themes/components/checkbox_cards.pyi +9 -7
- reflex/components/radix/themes/components/checkbox_group.pyi +9 -7
- reflex/components/radix/themes/components/context_menu.pyi +53 -40
- reflex/components/radix/themes/components/data_list.pyi +17 -13
- reflex/components/radix/themes/components/dialog.pyi +29 -22
- reflex/components/radix/themes/components/dropdown_menu.pyi +33 -25
- reflex/components/radix/themes/components/hover_card.pyi +17 -13
- reflex/components/radix/themes/components/icon_button.pyi +5 -4
- reflex/components/radix/themes/components/inset.pyi +5 -4
- reflex/components/radix/themes/components/popover.pyi +17 -13
- reflex/components/radix/themes/components/progress.pyi +5 -4
- reflex/components/radix/themes/components/radio.pyi +5 -4
- reflex/components/radix/themes/components/radio_cards.pyi +9 -7
- reflex/components/radix/themes/components/radio_group.pyi +17 -13
- reflex/components/radix/themes/components/scroll_area.pyi +5 -4
- reflex/components/radix/themes/components/segmented_control.pyi +9 -7
- reflex/components/radix/themes/components/select.pyi +37 -28
- reflex/components/radix/themes/components/separator.pyi +5 -4
- reflex/components/radix/themes/components/skeleton.pyi +5 -4
- reflex/components/radix/themes/components/slider.pyi +5 -4
- reflex/components/radix/themes/components/spinner.pyi +5 -4
- reflex/components/radix/themes/components/switch.pyi +5 -4
- reflex/components/radix/themes/components/table.pyi +29 -22
- reflex/components/radix/themes/components/tabs.pyi +21 -16
- reflex/components/radix/themes/components/text_area.pyi +5 -4
- reflex/components/radix/themes/components/text_field.pyi +13 -10
- reflex/components/radix/themes/components/tooltip.pyi +5 -4
- reflex/components/radix/themes/layout/base.pyi +5 -4
- reflex/components/radix/themes/layout/box.pyi +5 -4
- reflex/components/radix/themes/layout/center.pyi +5 -4
- reflex/components/radix/themes/layout/container.pyi +5 -4
- reflex/components/radix/themes/layout/flex.pyi +5 -4
- reflex/components/radix/themes/layout/grid.pyi +5 -4
- reflex/components/radix/themes/layout/list.pyi +21 -16
- reflex/components/radix/themes/layout/section.pyi +5 -4
- reflex/components/radix/themes/layout/spacer.pyi +5 -4
- reflex/components/radix/themes/layout/stack.pyi +13 -10
- reflex/components/radix/themes/typography/blockquote.pyi +5 -4
- reflex/components/radix/themes/typography/code.pyi +5 -4
- reflex/components/radix/themes/typography/heading.pyi +5 -4
- reflex/components/radix/themes/typography/link.py +42 -9
- reflex/components/radix/themes/typography/link.pyi +311 -6
- reflex/components/radix/themes/typography/text.pyi +29 -22
- reflex/components/react_player/audio.pyi +5 -4
- reflex/components/react_player/react_player.pyi +5 -4
- reflex/components/react_player/video.pyi +5 -4
- reflex/components/recharts/cartesian.py +2 -1
- reflex/components/recharts/cartesian.pyi +65 -46
- reflex/components/recharts/charts.py +4 -2
- reflex/components/recharts/charts.pyi +36 -24
- reflex/components/recharts/general.pyi +24 -18
- reflex/components/recharts/polar.py +8 -4
- reflex/components/recharts/polar.pyi +16 -10
- reflex/components/recharts/recharts.pyi +9 -7
- reflex/components/sonner/toast.py +2 -2
- reflex/components/sonner/toast.pyi +7 -6
- reflex/config.py +3 -77
- reflex/constants/__init__.py +1 -0
- reflex/constants/base.py +38 -8
- reflex/constants/compiler.py +4 -2
- reflex/constants/event.py +1 -0
- reflex/constants/installer.py +23 -16
- reflex/constants/state.py +2 -0
- reflex/custom_components/custom_components.py +0 -14
- reflex/environment.py +1 -1
- reflex/event.py +178 -81
- reflex/experimental/__init__.py +0 -30
- reflex/istate/proxy.py +5 -3
- reflex/page.py +0 -27
- reflex/plugins/__init__.py +3 -2
- reflex/plugins/base.py +5 -1
- reflex/plugins/shared_tailwind.py +158 -0
- reflex/plugins/sitemap.py +206 -0
- reflex/plugins/tailwind_v3.py +13 -106
- reflex/plugins/tailwind_v4.py +15 -108
- reflex/reflex.py +1 -0
- reflex/state.py +134 -140
- reflex/testing.py +57 -9
- reflex/utils/build.py +9 -69
- reflex/utils/exec.py +59 -161
- reflex/utils/export.py +1 -1
- reflex/utils/imports.py +0 -4
- reflex/utils/misc.py +28 -0
- reflex/utils/prerequisites.py +62 -59
- reflex/utils/processes.py +6 -6
- reflex/utils/pyi_generator.py +21 -9
- reflex/utils/serializers.py +14 -1
- reflex/utils/types.py +196 -61
- reflex/vars/__init__.py +2 -0
- reflex/vars/base.py +367 -134
- {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/METADATA +12 -5
- {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/RECORD +190 -196
- reflex/.templates/web/next.config.js +0 -7
- reflex/components/base/head.py +0 -20
- reflex/components/base/head.pyi +0 -116
- reflex/components/next/__init__.py +0 -10
- reflex/components/next/base.py +0 -7
- reflex/components/next/image.py +0 -117
- reflex/components/next/image.pyi +0 -94
- reflex/components/next/link.py +0 -20
- reflex/components/next/link.pyi +0 -67
- reflex/components/next/video.py +0 -38
- reflex/components/next/video.pyi +0 -68
- reflex/components/suneditor/__init__.py +0 -5
- reflex/components/suneditor/editor.py +0 -269
- reflex/components/suneditor/editor.pyi +0 -199
- reflex/experimental/layout.py +0 -254
- {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/WHEEL +0 -0
- {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.14a5.dist-info → reflex-0.8.0a1.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
|
-
|
|
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:
|
|
295
|
-
context:
|
|
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 =
|
|
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,10 +853,39 @@ 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
|
-
|
|
898
|
-
|
|
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.
|
|
@@ -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 ["/", "$/", "."
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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:
|
|
@@ -1293,15 +1245,6 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1293
1245
|
# Track imports found.
|
|
1294
1246
|
all_imports = {}
|
|
1295
1247
|
|
|
1296
|
-
# This has to happen before compiling stateful components as that
|
|
1297
|
-
# prevents recursive functions from reaching all components.
|
|
1298
|
-
for component in self._pages.values():
|
|
1299
|
-
# Add component._get_all_imports() to all_imports.
|
|
1300
|
-
all_imports.update(component._get_all_imports())
|
|
1301
|
-
|
|
1302
|
-
# Add the app wrappers from this component.
|
|
1303
|
-
app_wrappers.update(component._get_all_app_wrap_components())
|
|
1304
|
-
|
|
1305
1248
|
if (toaster := self.toaster) is not None:
|
|
1306
1249
|
from reflex.components.component import memo
|
|
1307
1250
|
|
|
@@ -1319,16 +1262,24 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1319
1262
|
if component is not None:
|
|
1320
1263
|
app_wrappers[key] = component
|
|
1321
1264
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1265
|
+
# Compile custom components.
|
|
1266
|
+
(
|
|
1267
|
+
custom_components_output,
|
|
1268
|
+
custom_components_result,
|
|
1269
|
+
custom_components_imports,
|
|
1270
|
+
) = compiler.compile_components(dict.fromkeys(CUSTOM_COMPONENTS.values()))
|
|
1271
|
+
compile_results.append((custom_components_output, custom_components_result))
|
|
1272
|
+
all_imports.update(custom_components_imports)
|
|
1273
|
+
progress.advance(task)
|
|
1274
|
+
|
|
1275
|
+
# This has to happen before compiling stateful components as that
|
|
1276
|
+
# prevents recursive functions from reaching all components.
|
|
1277
|
+
for component in self._pages.values():
|
|
1278
|
+
# Add component._get_all_imports() to all_imports.
|
|
1279
|
+
all_imports.update(component._get_all_imports())
|
|
1324
1280
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
reason="Use app_wraps instead.",
|
|
1328
|
-
deprecation_version="0.7.1",
|
|
1329
|
-
removal_version="0.8.0",
|
|
1330
|
-
)
|
|
1331
|
-
app_wrappers[(55, "ErrorBoundary")] = into_component(self.error_boundary)
|
|
1281
|
+
# Add the app wrappers from this component.
|
|
1282
|
+
app_wrappers.update(component._get_all_app_wrap_components())
|
|
1332
1283
|
|
|
1333
1284
|
# Perform auto-memoization of stateful components.
|
|
1334
1285
|
with console.timing("Auto-memoize StatefulComponents"):
|
|
@@ -1431,6 +1382,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1431
1382
|
)
|
|
1432
1383
|
)
|
|
1433
1384
|
),
|
|
1385
|
+
unevaluated_pages=list(self._unevaluated_pages.values()),
|
|
1434
1386
|
)
|
|
1435
1387
|
|
|
1436
1388
|
# Wait for all compilation tasks to complete.
|
|
@@ -1465,16 +1417,6 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
1465
1417
|
)
|
|
1466
1418
|
progress.advance(task)
|
|
1467
1419
|
|
|
1468
|
-
# Compile custom components.
|
|
1469
|
-
(
|
|
1470
|
-
custom_components_output,
|
|
1471
|
-
custom_components_result,
|
|
1472
|
-
custom_components_imports,
|
|
1473
|
-
) = compiler.compile_components(dict.fromkeys(CUSTOM_COMPONENTS.values()))
|
|
1474
|
-
compile_results.append((custom_components_output, custom_components_result))
|
|
1475
|
-
all_imports.update(custom_components_imports)
|
|
1476
|
-
|
|
1477
|
-
progress.advance(task)
|
|
1478
1420
|
progress.stop()
|
|
1479
1421
|
|
|
1480
1422
|
if dry_run:
|
|
@@ -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
|
|
1488
|
-
|
|
1489
|
-
|
|
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(
|
|
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:
|
|
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
|
reflex/compiler/compiler.py
CHANGED
|
@@ -21,12 +21,14 @@ from reflex.components.component import (
|
|
|
21
21
|
)
|
|
22
22
|
from reflex.config import get_config
|
|
23
23
|
from reflex.constants.compiler import PageNames
|
|
24
|
+
from reflex.constants.state import FIELD_MARKER
|
|
24
25
|
from reflex.environment import environment
|
|
25
26
|
from reflex.state import BaseState
|
|
26
27
|
from reflex.style import SYSTEM_COLOR_MODE
|
|
27
28
|
from reflex.utils import console, path_ops
|
|
28
29
|
from reflex.utils.exceptions import ReflexError
|
|
29
30
|
from reflex.utils.exec import is_prod_mode
|
|
31
|
+
from reflex.utils.format import to_title_case
|
|
30
32
|
from reflex.utils.imports import ImportVar
|
|
31
33
|
from reflex.utils.prerequisites import get_web_dir
|
|
32
34
|
from reflex.vars.base import LiteralVar, Var
|
|
@@ -469,7 +471,9 @@ def compile_document_root(
|
|
|
469
471
|
The path and code of the compiled document root.
|
|
470
472
|
"""
|
|
471
473
|
# Get the path for the output file.
|
|
472
|
-
output_path =
|
|
474
|
+
output_path = str(
|
|
475
|
+
get_web_dir() / constants.Dirs.PAGES / constants.PageNames.DOCUMENT_ROOT
|
|
476
|
+
)
|
|
473
477
|
|
|
474
478
|
# Create the document root.
|
|
475
479
|
document_root = utils.create_document_root(
|
|
@@ -491,7 +495,9 @@ def compile_app(app_root: Component) -> tuple[str, str]:
|
|
|
491
495
|
The path and code of the compiled app wrapper.
|
|
492
496
|
"""
|
|
493
497
|
# Get the path for the output file.
|
|
494
|
-
output_path =
|
|
498
|
+
output_path = str(
|
|
499
|
+
get_web_dir() / constants.Dirs.PAGES / constants.PageNames.APP_ROOT
|
|
500
|
+
)
|
|
495
501
|
|
|
496
502
|
# Compile the document root.
|
|
497
503
|
code = _compile_app(app_root)
|
|
@@ -606,7 +612,7 @@ def purge_web_pages_dir():
|
|
|
606
612
|
return
|
|
607
613
|
|
|
608
614
|
# Empty out the web pages directory.
|
|
609
|
-
utils.empty_dir(get_web_dir() / constants.Dirs.PAGES, keep_files=["
|
|
615
|
+
utils.empty_dir(get_web_dir() / constants.Dirs.PAGES, keep_files=["routes.js"])
|
|
610
616
|
|
|
611
617
|
|
|
612
618
|
if TYPE_CHECKING:
|
|
@@ -668,6 +674,32 @@ def readable_name_from_component(
|
|
|
668
674
|
return None
|
|
669
675
|
|
|
670
676
|
|
|
677
|
+
def _modify_exception(e: Exception) -> None:
|
|
678
|
+
"""Modify the exception to make it more readable.
|
|
679
|
+
|
|
680
|
+
Args:
|
|
681
|
+
e: The exception to modify.
|
|
682
|
+
"""
|
|
683
|
+
if len(e.args) == 1 and isinstance((msg := e.args[0]), str):
|
|
684
|
+
while (state_index := msg.find("reflex___")) != -1:
|
|
685
|
+
dot_index = msg.find(".", state_index)
|
|
686
|
+
if dot_index == -1:
|
|
687
|
+
break
|
|
688
|
+
state_name = msg[state_index:dot_index]
|
|
689
|
+
module_dot_state_name = state_name.replace("___", ".").rsplit("__", 1)[-1]
|
|
690
|
+
module_path, _, state_snake_case = module_dot_state_name.rpartition(".")
|
|
691
|
+
if not state_snake_case:
|
|
692
|
+
break
|
|
693
|
+
actual_state_name = to_title_case(state_snake_case)
|
|
694
|
+
msg = (
|
|
695
|
+
f"{msg[:state_index]}{module_path}.{actual_state_name}{msg[dot_index:]}"
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
msg = msg.replace(FIELD_MARKER, "")
|
|
699
|
+
|
|
700
|
+
e.args = (msg,)
|
|
701
|
+
|
|
702
|
+
|
|
671
703
|
def into_component(component: Component | ComponentCallable) -> Component:
|
|
672
704
|
"""Convert a component to a Component.
|
|
673
705
|
|
|
@@ -692,6 +724,7 @@ def into_component(component: Component | ComponentCallable) -> Component:
|
|
|
692
724
|
component_called = component()
|
|
693
725
|
except KeyError as e:
|
|
694
726
|
if isinstance(e, ReflexError):
|
|
727
|
+
_modify_exception(e)
|
|
695
728
|
raise
|
|
696
729
|
key = e.args[0] if e.args else None
|
|
697
730
|
if key is not None and isinstance(key, Var):
|
|
@@ -701,6 +734,7 @@ def into_component(component: Component | ComponentCallable) -> Component:
|
|
|
701
734
|
raise
|
|
702
735
|
except TypeError as e:
|
|
703
736
|
if isinstance(e, ReflexError):
|
|
737
|
+
_modify_exception(e)
|
|
704
738
|
raise
|
|
705
739
|
message = e.args[0] if e.args else None
|
|
706
740
|
if message and isinstance(message, str):
|
|
@@ -726,6 +760,9 @@ def into_component(component: Component | ComponentCallable) -> Component:
|
|
|
726
760
|
"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
761
|
).with_traceback(e.__traceback__) from None
|
|
728
762
|
raise
|
|
763
|
+
except ReflexError as e:
|
|
764
|
+
_modify_exception(e)
|
|
765
|
+
raise
|
|
729
766
|
|
|
730
767
|
if (converted := _into_component_once(component_called)) is not None:
|
|
731
768
|
return converted
|