reflex 0.6.4a3__py3-none-any.whl → 0.6.5__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/web/pages/custom_component.js.jinja2 +0 -14
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +4 -8
- reflex/.templates/web/components/shiki/code.js +16 -11
- reflex/.templates/web/utils/state.js +29 -21
- reflex/__init__.py +4 -0
- reflex/__init__.pyi +4 -0
- reflex/app.py +148 -154
- reflex/app_mixins/lifespan.py +5 -1
- reflex/app_mixins/middleware.py +3 -1
- reflex/app_mixins/mixin.py +3 -2
- reflex/base.py +2 -4
- reflex/compiler/compiler.py +111 -37
- reflex/components/base/app_wrap.pyi +17 -17
- reflex/components/base/bare.py +72 -3
- reflex/components/base/body.pyi +17 -17
- reflex/components/base/document.pyi +81 -81
- reflex/components/base/error_boundary.pyi +25 -18
- reflex/components/base/fragment.pyi +17 -17
- reflex/components/base/head.pyi +33 -33
- reflex/components/base/link.pyi +33 -33
- reflex/components/base/meta.pyi +65 -65
- reflex/components/base/script.py +4 -4
- reflex/components/base/script.pyi +23 -20
- reflex/components/component.py +250 -31
- reflex/components/core/banner.py +1 -1
- reflex/components/core/banner.pyi +81 -81
- reflex/components/core/client_side_routing.pyi +33 -33
- reflex/components/core/clipboard.py +2 -2
- reflex/components/core/clipboard.pyi +24 -18
- reflex/components/core/debounce.py +2 -2
- reflex/components/core/debounce.pyi +18 -18
- reflex/components/core/html.pyi +17 -17
- reflex/components/core/upload.py +90 -28
- reflex/components/core/upload.pyi +128 -72
- reflex/components/datadisplay/code.py +55 -40
- reflex/components/datadisplay/code.pyi +46 -44
- reflex/components/datadisplay/dataeditor.py +21 -20
- reflex/components/datadisplay/dataeditor.pyi +103 -35
- reflex/components/datadisplay/shiki_code_block.py +60 -27
- reflex/components/datadisplay/shiki_code_block.pyi +86 -65
- reflex/components/dynamic.py +9 -5
- reflex/components/el/element.pyi +17 -17
- reflex/components/el/elements/base.pyi +17 -17
- reflex/components/el/elements/forms.py +12 -3
- reflex/components/el/elements/forms.pyi +293 -233
- reflex/components/el/elements/inline.pyi +449 -449
- reflex/components/el/elements/media.pyi +401 -401
- reflex/components/el/elements/metadata.pyi +97 -97
- reflex/components/el/elements/other.pyi +113 -113
- reflex/components/el/elements/scripts.pyi +49 -49
- reflex/components/el/elements/sectioning.pyi +241 -241
- reflex/components/el/elements/tables.pyi +161 -161
- reflex/components/el/elements/typography.pyi +241 -241
- reflex/components/gridjs/datatable.pyi +33 -33
- reflex/components/lucide/icon.py +1 -1
- reflex/components/lucide/icon.pyi +33 -33
- reflex/components/markdown/markdown.py +180 -49
- reflex/components/markdown/markdown.pyi +36 -19
- reflex/components/moment/moment.py +17 -21
- reflex/components/moment/moment.pyi +26 -21
- reflex/components/next/base.pyi +17 -17
- reflex/components/next/image.py +3 -3
- reflex/components/next/image.pyi +21 -19
- reflex/components/next/link.pyi +17 -17
- reflex/components/next/video.pyi +17 -17
- reflex/components/plotly/plotly.py +79 -78
- reflex/components/plotly/plotly.pyi +91 -41
- reflex/components/props.py +34 -0
- reflex/components/radix/primitives/accordion.py +15 -8
- reflex/components/radix/primitives/accordion.pyi +121 -118
- reflex/components/radix/primitives/base.pyi +33 -33
- reflex/components/radix/primitives/drawer.py +41 -20
- reflex/components/radix/primitives/drawer.pyi +279 -190
- reflex/components/radix/primitives/form.py +2 -2
- reflex/components/radix/primitives/form.pyi +200 -167
- reflex/components/radix/primitives/progress.pyi +81 -81
- reflex/components/radix/primitives/slider.pyi +89 -83
- reflex/components/radix/themes/base.py +30 -1
- reflex/components/radix/themes/base.pyi +286 -113
- reflex/components/radix/themes/color_mode.py +17 -9
- reflex/components/radix/themes/color_mode.pyi +68 -56
- reflex/components/radix/themes/components/alert_dialog.py +8 -5
- reflex/components/radix/themes/components/alert_dialog.pyi +125 -117
- reflex/components/radix/themes/components/aspect_ratio.pyi +17 -17
- reflex/components/radix/themes/components/avatar.py +1 -5
- reflex/components/radix/themes/components/avatar.pyi +17 -17
- reflex/components/radix/themes/components/badge.py +1 -5
- reflex/components/radix/themes/components/badge.pyi +17 -17
- reflex/components/radix/themes/components/button.pyi +18 -21
- reflex/components/radix/themes/components/callout.py +1 -4
- reflex/components/radix/themes/components/callout.pyi +81 -81
- reflex/components/radix/themes/components/card.py +1 -3
- reflex/components/radix/themes/components/card.pyi +17 -17
- reflex/components/radix/themes/components/checkbox.py +4 -8
- reflex/components/radix/themes/components/checkbox.pyi +61 -52
- reflex/components/radix/themes/components/checkbox_cards.pyi +33 -33
- reflex/components/radix/themes/components/checkbox_group.pyi +33 -33
- reflex/components/radix/themes/components/context_menu.py +121 -28
- reflex/components/radix/themes/components/context_menu.pyi +250 -147
- reflex/components/radix/themes/components/data_list.pyi +65 -65
- reflex/components/radix/themes/components/dialog.py +11 -11
- reflex/components/radix/themes/components/dialog.pyi +135 -120
- reflex/components/radix/themes/components/dropdown_menu.py +14 -25
- reflex/components/radix/themes/components/dropdown_menu.pyi +157 -145
- reflex/components/radix/themes/components/hover_card.py +19 -7
- reflex/components/radix/themes/components/hover_card.pyi +102 -67
- reflex/components/radix/themes/components/icon_button.pyi +18 -21
- reflex/components/radix/themes/components/inset.py +1 -3
- reflex/components/radix/themes/components/inset.pyi +17 -17
- reflex/components/radix/themes/components/popover.py +22 -13
- reflex/components/radix/themes/components/popover.pyi +98 -72
- reflex/components/radix/themes/components/progress.pyi +17 -17
- reflex/components/radix/themes/components/radio.pyi +17 -17
- reflex/components/radix/themes/components/radio_cards.py +2 -2
- reflex/components/radix/themes/components/radio_cards.pyi +37 -34
- reflex/components/radix/themes/components/radio_group.py +3 -7
- reflex/components/radix/themes/components/radio_group.pyi +69 -66
- reflex/components/radix/themes/components/scroll_area.py +1 -3
- reflex/components/radix/themes/components/scroll_area.pyi +17 -17
- reflex/components/radix/themes/components/segmented_control.pyi +37 -34
- reflex/components/radix/themes/components/select.py +7 -11
- reflex/components/radix/themes/components/select.pyi +175 -154
- reflex/components/radix/themes/components/separator.py +1 -4
- reflex/components/radix/themes/components/separator.pyi +17 -17
- reflex/components/radix/themes/components/skeleton.pyi +17 -17
- reflex/components/radix/themes/components/slider.py +12 -21
- reflex/components/radix/themes/components/slider.pyi +47 -25
- reflex/components/radix/themes/components/spinner.py +1 -4
- reflex/components/radix/themes/components/spinner.pyi +17 -17
- reflex/components/radix/themes/components/switch.py +3 -6
- reflex/components/radix/themes/components/switch.pyi +21 -18
- reflex/components/radix/themes/components/table.py +21 -5
- reflex/components/radix/themes/components/table.pyi +392 -116
- reflex/components/radix/themes/components/tabs.py +3 -6
- reflex/components/radix/themes/components/tabs.pyi +89 -83
- reflex/components/radix/themes/components/text_area.py +1 -5
- reflex/components/radix/themes/components/text_area.pyi +43 -20
- reflex/components/radix/themes/components/text_field.py +1 -5
- reflex/components/radix/themes/components/text_field.pyi +101 -55
- reflex/components/radix/themes/components/tooltip.py +5 -7
- reflex/components/radix/themes/components/tooltip.pyi +25 -22
- reflex/components/radix/themes/layout/base.py +2 -27
- reflex/components/radix/themes/layout/base.pyi +82 -82
- reflex/components/radix/themes/layout/box.pyi +17 -17
- reflex/components/radix/themes/layout/center.pyi +17 -17
- reflex/components/radix/themes/layout/container.pyi +17 -17
- reflex/components/radix/themes/layout/flex.py +1 -6
- reflex/components/radix/themes/layout/flex.pyi +17 -17
- reflex/components/radix/themes/layout/grid.py +1 -6
- reflex/components/radix/themes/layout/grid.pyi +17 -17
- reflex/components/radix/themes/layout/list.py +20 -15
- reflex/components/radix/themes/layout/list.pyi +175 -92
- reflex/components/radix/themes/layout/section.pyi +17 -17
- reflex/components/radix/themes/layout/spacer.pyi +17 -17
- reflex/components/radix/themes/layout/stack.py +6 -6
- reflex/components/radix/themes/layout/stack.pyi +91 -62
- reflex/components/radix/themes/typography/blockquote.py +2 -8
- reflex/components/radix/themes/typography/blockquote.pyi +17 -17
- reflex/components/radix/themes/typography/code.py +4 -10
- reflex/components/radix/themes/typography/code.pyi +19 -18
- reflex/components/radix/themes/typography/heading.py +4 -11
- reflex/components/radix/themes/typography/heading.pyi +19 -18
- reflex/components/radix/themes/typography/link.py +4 -10
- reflex/components/radix/themes/typography/link.pyi +19 -18
- reflex/components/radix/themes/typography/text.py +4 -11
- reflex/components/radix/themes/typography/text.pyi +115 -114
- reflex/components/react_player/audio.pyi +58 -33
- reflex/components/react_player/react_player.py +17 -17
- reflex/components/react_player/react_player.pyi +55 -33
- reflex/components/react_player/video.pyi +58 -33
- reflex/components/recharts/cartesian.py +45 -45
- reflex/components/recharts/cartesian.pyi +389 -304
- reflex/components/recharts/charts.py +22 -22
- reflex/components/recharts/charts.pyi +226 -179
- reflex/components/recharts/general.py +26 -27
- reflex/components/recharts/general.pyi +106 -99
- reflex/components/recharts/polar.py +33 -33
- reflex/components/recharts/polar.pyi +70 -64
- reflex/components/recharts/recharts.pyi +33 -33
- reflex/components/sonner/toast.py +9 -36
- reflex/components/sonner/toast.pyi +20 -24
- reflex/components/suneditor/editor.py +8 -8
- reflex/components/suneditor/editor.pyi +50 -25
- reflex/components/tags/iter_tag.py +1 -10
- reflex/components/tags/tag.py +1 -4
- reflex/config.py +198 -35
- reflex/constants/__init__.py +4 -16
- reflex/constants/base.py +7 -14
- reflex/constants/colors.py +0 -1
- reflex/constants/installer.py +12 -7
- reflex/constants/state.py +4 -0
- reflex/custom_components/custom_components.py +6 -6
- reflex/event.py +486 -241
- reflex/experimental/client_state.py +9 -9
- reflex/experimental/layout.py +2 -2
- reflex/experimental/layout.pyi +95 -87
- reflex/experimental/misc.py +1 -1
- reflex/istate/__init__.py +1 -0
- reflex/istate/proxy.py +33 -0
- reflex/istate/wrappers.py +27 -0
- reflex/model.py +7 -7
- reflex/page.py +2 -1
- reflex/reflex.py +142 -8
- reflex/state.py +133 -46
- reflex/testing.py +9 -7
- reflex/utils/console.py +0 -1
- reflex/utils/exceptions.py +31 -3
- reflex/utils/exec.py +33 -14
- reflex/utils/format.py +15 -12
- reflex/utils/net.py +1 -1
- reflex/utils/path_ops.py +2 -2
- reflex/utils/prerequisites.py +82 -46
- reflex/utils/pyi_generator.py +63 -20
- reflex/utils/registry.py +1 -1
- reflex/utils/serializers.py +75 -36
- reflex/utils/telemetry.py +3 -2
- reflex/utils/types.py +125 -10
- reflex/vars/base.py +131 -119
- reflex/vars/function.py +59 -12
- reflex/vars/number.py +3 -1
- reflex/vars/object.py +30 -24
- reflex/vars/sequence.py +7 -7
- {reflex-0.6.4a3.dist-info → reflex-0.6.5.dist-info}/METADATA +3 -3
- reflex-0.6.5.dist-info/RECORD +394 -0
- reflex-0.6.4a3.dist-info/RECORD +0 -391
- {reflex-0.6.4a3.dist-info → reflex-0.6.5.dist-info}/LICENSE +0 -0
- {reflex-0.6.4a3.dist-info → reflex-0.6.5.dist-info}/WHEEL +0 -0
- {reflex-0.6.4a3.dist-info → reflex-0.6.5.dist-info}/entry_points.txt +0 -0
reflex/app.py
CHANGED
|
@@ -6,23 +6,25 @@ import asyncio
|
|
|
6
6
|
import concurrent.futures
|
|
7
7
|
import contextlib
|
|
8
8
|
import copy
|
|
9
|
+
import dataclasses
|
|
9
10
|
import functools
|
|
10
11
|
import inspect
|
|
11
12
|
import io
|
|
12
13
|
import json
|
|
13
14
|
import multiprocessing
|
|
14
|
-
import os
|
|
15
15
|
import platform
|
|
16
16
|
import sys
|
|
17
17
|
import traceback
|
|
18
18
|
from datetime import datetime
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from typing import (
|
|
21
|
+
TYPE_CHECKING,
|
|
21
22
|
Any,
|
|
22
23
|
AsyncIterator,
|
|
23
24
|
Callable,
|
|
24
25
|
Coroutine,
|
|
25
26
|
Dict,
|
|
27
|
+
Generic,
|
|
26
28
|
List,
|
|
27
29
|
Optional,
|
|
28
30
|
Set,
|
|
@@ -44,10 +46,9 @@ from starlette_admin.contrib.sqla.view import ModelView
|
|
|
44
46
|
from reflex import constants
|
|
45
47
|
from reflex.admin import AdminDash
|
|
46
48
|
from reflex.app_mixins import AppMixin, LifespanMixin, MiddlewareMixin
|
|
47
|
-
from reflex.base import Base
|
|
48
49
|
from reflex.compiler import compiler
|
|
49
50
|
from reflex.compiler import utils as compiler_utils
|
|
50
|
-
from reflex.compiler.compiler import ExecutorSafeFunctions
|
|
51
|
+
from reflex.compiler.compiler import ExecutorSafeFunctions, compile_theme
|
|
51
52
|
from reflex.components.base.app_wrap import AppWrap
|
|
52
53
|
from reflex.components.base.error_boundary import ErrorBoundary
|
|
53
54
|
from reflex.components.base.fragment import Fragment
|
|
@@ -65,11 +66,17 @@ from reflex.components.core.client_side_routing import (
|
|
|
65
66
|
from reflex.components.core.upload import Upload, get_upload_dir
|
|
66
67
|
from reflex.components.radix import themes
|
|
67
68
|
from reflex.config import environment, get_config
|
|
68
|
-
from reflex.event import
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
from reflex.event import (
|
|
70
|
+
BASE_STATE,
|
|
71
|
+
Event,
|
|
72
|
+
EventHandler,
|
|
73
|
+
EventSpec,
|
|
74
|
+
EventType,
|
|
75
|
+
IndividualEventType,
|
|
76
|
+
window_alert,
|
|
72
77
|
)
|
|
78
|
+
from reflex.model import Model, get_db_status
|
|
79
|
+
from reflex.page import DECORATED_PAGES
|
|
73
80
|
from reflex.route import (
|
|
74
81
|
get_route_args,
|
|
75
82
|
replace_brackets_with_keywords,
|
|
@@ -85,9 +92,12 @@ from reflex.state import (
|
|
|
85
92
|
code_uses_state_contexts,
|
|
86
93
|
)
|
|
87
94
|
from reflex.utils import codespaces, console, exceptions, format, prerequisites, types
|
|
88
|
-
from reflex.utils.exec import is_prod_mode, is_testing_env
|
|
95
|
+
from reflex.utils.exec import is_prod_mode, is_testing_env
|
|
89
96
|
from reflex.utils.imports import ImportVar
|
|
90
97
|
|
|
98
|
+
if TYPE_CHECKING:
|
|
99
|
+
from reflex.vars import Var
|
|
100
|
+
|
|
91
101
|
# Define custom types.
|
|
92
102
|
ComponentCallable = Callable[[], Component]
|
|
93
103
|
Reducer = Callable[[Event], Coroutine[Any, Any, StateUpdate]]
|
|
@@ -170,7 +180,23 @@ class OverlayFragment(Fragment):
|
|
|
170
180
|
pass
|
|
171
181
|
|
|
172
182
|
|
|
173
|
-
|
|
183
|
+
@dataclasses.dataclass(
|
|
184
|
+
frozen=True,
|
|
185
|
+
)
|
|
186
|
+
class UnevaluatedPage(Generic[BASE_STATE]):
|
|
187
|
+
"""An uncompiled page."""
|
|
188
|
+
|
|
189
|
+
component: Union[Component, ComponentCallable]
|
|
190
|
+
route: str
|
|
191
|
+
title: Union[Var, str, None]
|
|
192
|
+
description: Union[Var, str, None]
|
|
193
|
+
image: str
|
|
194
|
+
on_load: Union[EventType[[], BASE_STATE], None]
|
|
195
|
+
meta: List[Dict[str, str]]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@dataclasses.dataclass()
|
|
199
|
+
class App(MiddlewareMixin, LifespanMixin):
|
|
174
200
|
"""The main Reflex app that encapsulates the backend and frontend.
|
|
175
201
|
|
|
176
202
|
Every Reflex app needs an app defined in its main module.
|
|
@@ -192,24 +218,26 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
192
218
|
"""
|
|
193
219
|
|
|
194
220
|
# The global [theme](https://reflex.dev/docs/styling/theming/#theme) for the entire app.
|
|
195
|
-
theme: Optional[Component] =
|
|
221
|
+
theme: Optional[Component] = dataclasses.field(
|
|
222
|
+
default_factory=lambda: themes.theme(accent_color="blue")
|
|
223
|
+
)
|
|
196
224
|
|
|
197
225
|
# The [global style](https://reflex.dev/docs/styling/overview/#global-styles}) for the app.
|
|
198
|
-
style: ComponentStyle =
|
|
226
|
+
style: ComponentStyle = dataclasses.field(default_factory=dict)
|
|
199
227
|
|
|
200
228
|
# A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app.
|
|
201
|
-
stylesheets: List[str] =
|
|
229
|
+
stylesheets: List[str] = dataclasses.field(default_factory=list)
|
|
202
230
|
|
|
203
231
|
# A component that is present on every page (defaults to the Connection Error banner).
|
|
204
232
|
overlay_component: Optional[Union[Component, ComponentCallable]] = (
|
|
205
|
-
default_overlay_component
|
|
233
|
+
dataclasses.field(default_factory=default_overlay_component)
|
|
206
234
|
)
|
|
207
235
|
|
|
208
236
|
# Error boundary component to wrap the app with.
|
|
209
237
|
error_boundary: Optional[ComponentCallable] = default_error_boundary
|
|
210
238
|
|
|
211
239
|
# Components to add to the head of every page.
|
|
212
|
-
head_components: List[Component] =
|
|
240
|
+
head_components: List[Component] = dataclasses.field(default_factory=list)
|
|
213
241
|
|
|
214
242
|
# The Socket.IO AsyncServer instance.
|
|
215
243
|
sio: Optional[AsyncServer] = None
|
|
@@ -220,8 +248,13 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
220
248
|
# Attributes to add to the html root tag of every page.
|
|
221
249
|
html_custom_attrs: Optional[Dict[str, str]] = None
|
|
222
250
|
|
|
251
|
+
# A map from a route to an unevaluated page. PRIVATE.
|
|
252
|
+
unevaluated_pages: Dict[str, UnevaluatedPage] = dataclasses.field(
|
|
253
|
+
default_factory=dict
|
|
254
|
+
)
|
|
255
|
+
|
|
223
256
|
# A map from a page route to the component to render. Users should use `add_page`. PRIVATE.
|
|
224
|
-
pages: Dict[str, Component] =
|
|
257
|
+
pages: Dict[str, Component] = dataclasses.field(default_factory=dict)
|
|
225
258
|
|
|
226
259
|
# The backend API object. PRIVATE.
|
|
227
260
|
api: FastAPI = None # type: ignore
|
|
@@ -233,7 +266,9 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
233
266
|
_state_manager: Optional[StateManager] = None
|
|
234
267
|
|
|
235
268
|
# Mapping from a route to event handlers to trigger when the page loads. PRIVATE.
|
|
236
|
-
load_events: Dict[str, List[
|
|
269
|
+
load_events: Dict[str, List[IndividualEventType[[], Any]]] = dataclasses.field(
|
|
270
|
+
default_factory=dict
|
|
271
|
+
)
|
|
237
272
|
|
|
238
273
|
# Admin dashboard to view and manage the database. PRIVATE.
|
|
239
274
|
admin_dash: Optional[AdminDash] = None
|
|
@@ -242,7 +277,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
242
277
|
event_namespace: Optional[EventNamespace] = None
|
|
243
278
|
|
|
244
279
|
# Background tasks that are currently running. PRIVATE.
|
|
245
|
-
background_tasks: Set[asyncio.Task] = set
|
|
280
|
+
background_tasks: Set[asyncio.Task] = dataclasses.field(default_factory=set)
|
|
246
281
|
|
|
247
282
|
# Frontend Error Handler Function
|
|
248
283
|
frontend_exception_handler: Callable[[Exception], None] = (
|
|
@@ -254,23 +289,14 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
254
289
|
[Exception], Union[EventSpec, List[EventSpec], None]
|
|
255
290
|
] = default_backend_exception_handler
|
|
256
291
|
|
|
257
|
-
def
|
|
292
|
+
def __post_init__(self):
|
|
258
293
|
"""Initialize the app.
|
|
259
294
|
|
|
260
|
-
Args:
|
|
261
|
-
**kwargs: Kwargs to initialize the app with.
|
|
262
|
-
|
|
263
295
|
Raises:
|
|
264
296
|
ValueError: If the event namespace is not provided in the config.
|
|
265
297
|
Also, if there are multiple client subclasses of rx.BaseState(Subclasses of rx.BaseState should consist
|
|
266
298
|
of the DefaultState and the client app state).
|
|
267
299
|
"""
|
|
268
|
-
if "connect_error_component" in kwargs:
|
|
269
|
-
raise ValueError(
|
|
270
|
-
"`connect_error_component` is deprecated, use `overlay_component` instead"
|
|
271
|
-
)
|
|
272
|
-
super().__init__(**kwargs)
|
|
273
|
-
|
|
274
300
|
# Special case to allow test cases have multiple subclasses of rx.BaseState.
|
|
275
301
|
if not is_testing_env() and BaseState.__subclasses__() != [State]:
|
|
276
302
|
# Only rx.State is allowed as Base State subclass.
|
|
@@ -381,8 +407,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
381
407
|
|
|
382
408
|
def _add_optional_endpoints(self):
|
|
383
409
|
"""Add optional api endpoints (_upload)."""
|
|
384
|
-
# To upload files.
|
|
385
410
|
if Upload.is_used:
|
|
411
|
+
# To upload files.
|
|
386
412
|
self.api.post(str(constants.Endpoint.UPLOAD))(upload(self))
|
|
387
413
|
|
|
388
414
|
# To access uploaded files.
|
|
@@ -442,12 +468,10 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
442
468
|
self,
|
|
443
469
|
component: Component | ComponentCallable,
|
|
444
470
|
route: str | None = None,
|
|
445
|
-
title: str | None = None,
|
|
446
|
-
description: str | None = None,
|
|
471
|
+
title: str | Var | None = None,
|
|
472
|
+
description: str | Var | None = None,
|
|
447
473
|
image: str = constants.DefaultPage.IMAGE,
|
|
448
|
-
on_load:
|
|
449
|
-
EventHandler | EventSpec | list[EventHandler | EventSpec] | None
|
|
450
|
-
) = None,
|
|
474
|
+
on_load: EventType[[], BASE_STATE] | None = None,
|
|
451
475
|
meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
|
|
452
476
|
):
|
|
453
477
|
"""Add a page to the app.
|
|
@@ -479,13 +503,13 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
479
503
|
# Check if the route given is valid
|
|
480
504
|
verify_route_validity(route)
|
|
481
505
|
|
|
482
|
-
if route in self.
|
|
506
|
+
if route in self.unevaluated_pages and environment.RELOAD_CONFIG.is_set():
|
|
483
507
|
# when the app is reloaded(typically for app harness tests), we should maintain
|
|
484
508
|
# the latest render function of a route.This applies typically to decorated pages
|
|
485
509
|
# since they are only added when app._compile is called.
|
|
486
|
-
self.
|
|
510
|
+
self.unevaluated_pages.pop(route)
|
|
487
511
|
|
|
488
|
-
if route in self.
|
|
512
|
+
if route in self.unevaluated_pages:
|
|
489
513
|
route_name = (
|
|
490
514
|
f"`{route}` or `/`"
|
|
491
515
|
if route == constants.PageNames.INDEX_ROUTE
|
|
@@ -501,59 +525,39 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
501
525
|
state = self.state if self.state else State
|
|
502
526
|
state.setup_dynamic_args(get_route_args(route))
|
|
503
527
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if isinstance(component, tuple):
|
|
509
|
-
component = Fragment.create(*component)
|
|
510
|
-
|
|
511
|
-
# Ensure state is enabled if this page uses state.
|
|
512
|
-
if self.state is None:
|
|
513
|
-
if on_load or component._has_stateful_event_triggers():
|
|
514
|
-
self._enable_state()
|
|
515
|
-
else:
|
|
516
|
-
for var in component._get_vars(include_children=True):
|
|
517
|
-
var_data = var._get_all_var_data()
|
|
518
|
-
if not var_data:
|
|
519
|
-
continue
|
|
520
|
-
if not var_data.state:
|
|
521
|
-
continue
|
|
522
|
-
self._enable_state()
|
|
523
|
-
break
|
|
524
|
-
|
|
525
|
-
component = OverlayFragment.create(component)
|
|
528
|
+
if on_load:
|
|
529
|
+
self.load_events[route] = (
|
|
530
|
+
on_load if isinstance(on_load, list) else [on_load]
|
|
531
|
+
)
|
|
526
532
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
533
|
+
self.unevaluated_pages[route] = UnevaluatedPage(
|
|
534
|
+
component=component,
|
|
535
|
+
route=route,
|
|
536
|
+
title=title,
|
|
537
|
+
description=description,
|
|
538
|
+
image=image,
|
|
539
|
+
on_load=on_load,
|
|
540
|
+
meta=meta,
|
|
541
|
+
)
|
|
536
542
|
|
|
537
|
-
|
|
538
|
-
|
|
543
|
+
def _compile_page(self, route: str):
|
|
544
|
+
"""Compile a page.
|
|
539
545
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
546
|
+
Args:
|
|
547
|
+
route: The route of the page to compile.
|
|
548
|
+
"""
|
|
549
|
+
component, enable_state = compiler.compile_unevaluated_page(
|
|
550
|
+
route, self.unevaluated_pages[route], self.state, self.style, self.theme
|
|
544
551
|
)
|
|
545
552
|
|
|
553
|
+
if enable_state:
|
|
554
|
+
self._enable_state()
|
|
555
|
+
|
|
546
556
|
# Add the page.
|
|
547
557
|
self._check_routes_conflict(route)
|
|
548
558
|
self.pages[route] = component
|
|
549
559
|
|
|
550
|
-
|
|
551
|
-
if on_load:
|
|
552
|
-
if not isinstance(on_load, list):
|
|
553
|
-
on_load = [on_load]
|
|
554
|
-
self.load_events[route] = on_load
|
|
555
|
-
|
|
556
|
-
def get_load_events(self, route: str) -> list[EventHandler | EventSpec]:
|
|
560
|
+
def get_load_events(self, route: str) -> list[IndividualEventType[[], Any]]:
|
|
557
561
|
"""Get the load events for a route.
|
|
558
562
|
|
|
559
563
|
Args:
|
|
@@ -612,9 +616,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
612
616
|
title: str = constants.Page404.TITLE,
|
|
613
617
|
image: str = constants.Page404.IMAGE,
|
|
614
618
|
description: str = constants.Page404.DESCRIPTION,
|
|
615
|
-
on_load:
|
|
616
|
-
EventHandler | EventSpec | list[EventHandler | EventSpec] | None
|
|
617
|
-
) = None,
|
|
619
|
+
on_load: EventType[[], BASE_STATE] | None = None,
|
|
618
620
|
meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
|
|
619
621
|
):
|
|
620
622
|
"""Define a custom 404 page for any url having no match.
|
|
@@ -718,7 +720,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
718
720
|
Whether the app should be compiled.
|
|
719
721
|
"""
|
|
720
722
|
# Check the environment variable.
|
|
721
|
-
if
|
|
723
|
+
if environment.REFLEX_SKIP_COMPILE.get():
|
|
722
724
|
return False
|
|
723
725
|
|
|
724
726
|
nocompile = prerequisites.get_web_dir() / constants.NOCOMPILE_FILE
|
|
@@ -827,13 +829,33 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
827
829
|
"""
|
|
828
830
|
from reflex.utils.exceptions import ReflexRuntimeError
|
|
829
831
|
|
|
832
|
+
self.pages = {}
|
|
833
|
+
|
|
830
834
|
def get_compilation_time() -> str:
|
|
831
835
|
return str(datetime.now().time()).split(".")[0]
|
|
832
836
|
|
|
833
837
|
# Render a default 404 page if the user didn't supply one
|
|
834
|
-
if constants.Page404.SLUG not in self.
|
|
838
|
+
if constants.Page404.SLUG not in self.unevaluated_pages:
|
|
835
839
|
self.add_custom_404_page()
|
|
836
840
|
|
|
841
|
+
# Fix up the style.
|
|
842
|
+
self.style = evaluate_style_namespaces(self.style)
|
|
843
|
+
|
|
844
|
+
# Add the app wrappers.
|
|
845
|
+
app_wrappers: Dict[tuple[int, str], Component] = {
|
|
846
|
+
# Default app wrap component renders {children}
|
|
847
|
+
(0, "AppWrap"): AppWrap.create()
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if self.theme is not None:
|
|
851
|
+
# If a theme component was provided, wrap the app with it
|
|
852
|
+
app_wrappers[(20, "Theme")] = self.theme
|
|
853
|
+
# Fix #2992 by removing the top-level appearance prop
|
|
854
|
+
self.theme.appearance = None
|
|
855
|
+
|
|
856
|
+
for route in self.unevaluated_pages:
|
|
857
|
+
self._compile_page(route)
|
|
858
|
+
|
|
837
859
|
# Add the optional endpoints (_upload)
|
|
838
860
|
self._add_optional_endpoints()
|
|
839
861
|
|
|
@@ -868,28 +890,15 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
868
890
|
# Store the compile results.
|
|
869
891
|
compile_results = []
|
|
870
892
|
|
|
871
|
-
# Add the app wrappers.
|
|
872
|
-
app_wrappers: Dict[tuple[int, str], Component] = {
|
|
873
|
-
# Default app wrap component renders {children}
|
|
874
|
-
(0, "AppWrap"): AppWrap.create()
|
|
875
|
-
}
|
|
876
|
-
if self.theme is not None:
|
|
877
|
-
# If a theme component was provided, wrap the app with it
|
|
878
|
-
app_wrappers[(20, "Theme")] = self.theme
|
|
879
|
-
|
|
880
893
|
progress.advance(task)
|
|
881
894
|
|
|
882
|
-
# Fix up the style.
|
|
883
|
-
self.style = evaluate_style_namespaces(self.style)
|
|
884
|
-
|
|
885
895
|
# Track imports and custom components found.
|
|
886
896
|
all_imports = {}
|
|
887
897
|
custom_components = set()
|
|
888
898
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
899
|
+
# This has to happen before compiling stateful components as that
|
|
900
|
+
# prevents recursive functions from reaching all components.
|
|
901
|
+
for component in self.pages.values():
|
|
893
902
|
# Add component._get_all_imports() to all_imports.
|
|
894
903
|
all_imports.update(component._get_all_imports())
|
|
895
904
|
|
|
@@ -899,8 +908,6 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
899
908
|
# Add the custom components from the page to the set.
|
|
900
909
|
custom_components |= component._get_all_custom_components()
|
|
901
910
|
|
|
902
|
-
progress.advance(task)
|
|
903
|
-
|
|
904
911
|
# Perform auto-memoization of stateful components.
|
|
905
912
|
(
|
|
906
913
|
stateful_components_path,
|
|
@@ -918,6 +925,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
918
925
|
)
|
|
919
926
|
compile_results.append((stateful_components_path, stateful_components_code))
|
|
920
927
|
|
|
928
|
+
progress.advance(task)
|
|
929
|
+
|
|
921
930
|
# Compile the root document before fork.
|
|
922
931
|
compile_results.append(
|
|
923
932
|
compiler.compile_document_root(
|
|
@@ -927,37 +936,14 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
927
936
|
)
|
|
928
937
|
)
|
|
929
938
|
|
|
930
|
-
# Compile the contexts before fork.
|
|
931
|
-
compile_results.append(
|
|
932
|
-
compiler.compile_contexts(self.state, self.theme),
|
|
933
|
-
)
|
|
934
|
-
# Fix #2992 by removing the top-level appearance prop
|
|
935
|
-
if self.theme is not None:
|
|
936
|
-
self.theme.appearance = None
|
|
937
|
-
|
|
938
|
-
app_root = self._app_root(app_wrappers=app_wrappers)
|
|
939
|
-
|
|
940
939
|
progress.advance(task)
|
|
941
940
|
|
|
942
|
-
# Prepopulate the global ExecutorSafeFunctions class with input data required by the compile functions.
|
|
943
|
-
# This is required for multiprocessing to work, in presence of non-picklable inputs.
|
|
944
|
-
for route, component in zip(self.pages, page_components):
|
|
945
|
-
ExecutorSafeFunctions.COMPILE_PAGE_ARGS_BY_ROUTE[route] = (
|
|
946
|
-
route,
|
|
947
|
-
component,
|
|
948
|
-
self.state,
|
|
949
|
-
)
|
|
950
|
-
|
|
951
|
-
ExecutorSafeFunctions.COMPILE_APP_APP_ROOT = app_root
|
|
952
|
-
ExecutorSafeFunctions.CUSTOM_COMPONENTS = custom_components
|
|
953
|
-
ExecutorSafeFunctions.STYLE = self.style
|
|
954
|
-
|
|
955
941
|
# Use a forking process pool, if possible. Much faster, especially for large sites.
|
|
956
942
|
# Fallback to ThreadPoolExecutor as something that will always work.
|
|
957
943
|
executor = None
|
|
958
944
|
if (
|
|
959
945
|
platform.system() in ("Linux", "Darwin")
|
|
960
|
-
and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES)
|
|
946
|
+
and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES.get())
|
|
961
947
|
is not None
|
|
962
948
|
):
|
|
963
949
|
executor = concurrent.futures.ProcessPoolExecutor(
|
|
@@ -966,39 +952,34 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
966
952
|
)
|
|
967
953
|
else:
|
|
968
954
|
executor = concurrent.futures.ThreadPoolExecutor(
|
|
969
|
-
max_workers=environment.REFLEX_COMPILE_THREADS
|
|
955
|
+
max_workers=environment.REFLEX_COMPILE_THREADS.get()
|
|
970
956
|
)
|
|
971
957
|
|
|
958
|
+
for route, component in zip(self.pages, page_components):
|
|
959
|
+
ExecutorSafeFunctions.COMPONENTS[route] = component
|
|
960
|
+
|
|
961
|
+
ExecutorSafeFunctions.STATE = self.state
|
|
962
|
+
|
|
972
963
|
with executor:
|
|
973
964
|
result_futures = []
|
|
974
|
-
custom_components_future = None
|
|
975
|
-
|
|
976
|
-
def _mark_complete(_=None):
|
|
977
|
-
progress.advance(task)
|
|
978
965
|
|
|
979
966
|
def _submit_work(fn, *args, **kwargs):
|
|
980
967
|
f = executor.submit(fn, *args, **kwargs)
|
|
981
|
-
f.
|
|
968
|
+
# f = executor.apipe(fn, *args, **kwargs)
|
|
982
969
|
result_futures.append(f)
|
|
983
970
|
|
|
984
|
-
# Compile
|
|
971
|
+
# Compile the pre-compiled pages.
|
|
985
972
|
for route in self.pages:
|
|
986
|
-
_submit_work(
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
# Compile the custom components.
|
|
992
|
-
custom_components_future = executor.submit(
|
|
993
|
-
ExecutorSafeFunctions.compile_custom_components,
|
|
994
|
-
)
|
|
995
|
-
custom_components_future.add_done_callback(_mark_complete)
|
|
973
|
+
_submit_work(
|
|
974
|
+
ExecutorSafeFunctions.compile_page,
|
|
975
|
+
route,
|
|
976
|
+
)
|
|
996
977
|
|
|
997
978
|
# Compile the root stylesheet with base styles.
|
|
998
979
|
_submit_work(compiler.compile_root_stylesheet, self.stylesheets)
|
|
999
980
|
|
|
1000
981
|
# Compile the theme.
|
|
1001
|
-
_submit_work(
|
|
982
|
+
_submit_work(compile_theme, self.style)
|
|
1002
983
|
|
|
1003
984
|
# Compile the Tailwind config.
|
|
1004
985
|
if config.tailwind is not None:
|
|
@@ -1012,21 +993,34 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
|
|
1012
993
|
# Wait for all compilation tasks to complete.
|
|
1013
994
|
for future in concurrent.futures.as_completed(result_futures):
|
|
1014
995
|
compile_results.append(future.result())
|
|
996
|
+
progress.advance(task)
|
|
1015
997
|
|
|
1016
|
-
|
|
1017
|
-
# to install proper frontend packages.
|
|
1018
|
-
(
|
|
1019
|
-
*custom_components_result,
|
|
1020
|
-
custom_components_imports,
|
|
1021
|
-
) = custom_components_future.result()
|
|
1022
|
-
compile_results.append(custom_components_result)
|
|
1023
|
-
all_imports.update(custom_components_imports)
|
|
998
|
+
app_root = self._app_root(app_wrappers=app_wrappers)
|
|
1024
999
|
|
|
1025
1000
|
# Get imports from AppWrap components.
|
|
1026
1001
|
all_imports.update(app_root._get_all_imports())
|
|
1027
1002
|
|
|
1028
1003
|
progress.advance(task)
|
|
1029
1004
|
|
|
1005
|
+
# Compile the contexts.
|
|
1006
|
+
compile_results.append(
|
|
1007
|
+
compiler.compile_contexts(self.state, self.theme),
|
|
1008
|
+
)
|
|
1009
|
+
progress.advance(task)
|
|
1010
|
+
|
|
1011
|
+
# Compile the app root.
|
|
1012
|
+
compile_results.append(
|
|
1013
|
+
compiler.compile_app(app_root),
|
|
1014
|
+
)
|
|
1015
|
+
progress.advance(task)
|
|
1016
|
+
|
|
1017
|
+
# Compile custom components.
|
|
1018
|
+
*custom_components_result, custom_components_imports = (
|
|
1019
|
+
compiler.compile_components(custom_components)
|
|
1020
|
+
)
|
|
1021
|
+
compile_results.append(custom_components_result)
|
|
1022
|
+
all_imports.update(custom_components_imports)
|
|
1023
|
+
|
|
1030
1024
|
progress.advance(task)
|
|
1031
1025
|
progress.stop()
|
|
1032
1026
|
|
|
@@ -1393,7 +1387,7 @@ def upload(app: App):
|
|
|
1393
1387
|
if isinstance(func, EventHandler):
|
|
1394
1388
|
if func.is_background:
|
|
1395
1389
|
raise UploadTypeError(
|
|
1396
|
-
f"@rx.background is not supported for upload handler `{handler}`.",
|
|
1390
|
+
f"@rx.event(background=True) is not supported for upload handler `{handler}`.",
|
|
1397
1391
|
)
|
|
1398
1392
|
func = func.fn
|
|
1399
1393
|
if isinstance(func, functools.partial):
|
reflex/app_mixins/lifespan.py
CHANGED
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import contextlib
|
|
7
|
+
import dataclasses
|
|
7
8
|
import functools
|
|
8
9
|
import inspect
|
|
9
10
|
from typing import Callable, Coroutine, Set, Union
|
|
@@ -16,11 +17,14 @@ from reflex.utils.exceptions import InvalidLifespanTaskType
|
|
|
16
17
|
from .mixin import AppMixin
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
@dataclasses.dataclass
|
|
19
21
|
class LifespanMixin(AppMixin):
|
|
20
22
|
"""A Mixin that allow tasks to run during the whole app lifespan."""
|
|
21
23
|
|
|
22
24
|
# Lifespan tasks that are planned to run.
|
|
23
|
-
lifespan_tasks: Set[Union[asyncio.Task, Callable]] =
|
|
25
|
+
lifespan_tasks: Set[Union[asyncio.Task, Callable]] = dataclasses.field(
|
|
26
|
+
default_factory=set
|
|
27
|
+
)
|
|
24
28
|
|
|
25
29
|
@contextlib.asynccontextmanager
|
|
26
30
|
async def _run_lifespan_tasks(self, app: FastAPI):
|
reflex/app_mixins/middleware.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import dataclasses
|
|
6
7
|
from typing import List
|
|
7
8
|
|
|
8
9
|
from reflex.event import Event
|
|
@@ -12,11 +13,12 @@ from reflex.state import BaseState, StateUpdate
|
|
|
12
13
|
from .mixin import AppMixin
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
@dataclasses.dataclass
|
|
15
17
|
class MiddlewareMixin(AppMixin):
|
|
16
18
|
"""Middleware Mixin that allow to add middleware to the app."""
|
|
17
19
|
|
|
18
20
|
# Middleware to add to the app. Users should use `add_middleware`. PRIVATE.
|
|
19
|
-
middleware: List[Middleware] =
|
|
21
|
+
middleware: List[Middleware] = dataclasses.field(default_factory=list)
|
|
20
22
|
|
|
21
23
|
def _init_mixin(self):
|
|
22
24
|
self.middleware.append(HydrateMiddleware())
|
reflex/app_mixins/mixin.py
CHANGED
reflex/base.py
CHANGED
|
@@ -16,9 +16,6 @@ except ModuleNotFoundError:
|
|
|
16
16
|
from pydantic.fields import ModelField # type: ignore
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
from reflex import constants
|
|
20
|
-
|
|
21
|
-
|
|
22
19
|
def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None:
|
|
23
20
|
"""Ensure that the field's name does not shadow an existing attribute of the model.
|
|
24
21
|
|
|
@@ -31,7 +28,8 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
|
|
|
31
28
|
"""
|
|
32
29
|
from reflex.utils.exceptions import VarNameError
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
# can't use reflex.config.environment here cause of circular import
|
|
32
|
+
reload = os.getenv("__RELOAD_CONFIG", "").lower() == "true"
|
|
35
33
|
for base in bases:
|
|
36
34
|
try:
|
|
37
35
|
if not reload and getattr(base, field_name, None):
|