reflex 0.6.8a2__py3-none-any.whl → 0.7.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/custom_components/pyproject.toml.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +7 -7
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -4
- reflex/.templates/web/utils/state.js +65 -36
- reflex/__init__.py +4 -17
- reflex/__init__.pyi +1 -2
- reflex/app.py +244 -109
- reflex/app_mixins/lifespan.py +9 -9
- reflex/app_mixins/middleware.py +6 -6
- reflex/app_module_for_backend.py +3 -7
- reflex/base.py +7 -7
- reflex/compiler/compiler.py +8 -0
- reflex/compiler/utils.py +35 -6
- reflex/components/base/bare.py +1 -1
- reflex/components/base/error_boundary.py +2 -1
- reflex/components/base/error_boundary.pyi +2 -1
- reflex/components/base/meta.py +2 -2
- reflex/components/base/strict_mode.py +10 -0
- reflex/components/base/strict_mode.pyi +57 -0
- reflex/components/component.py +38 -77
- reflex/components/core/banner.py +83 -4
- reflex/components/core/banner.pyi +86 -0
- reflex/components/core/breakpoints.py +3 -1
- reflex/components/core/client_side_routing.py +1 -1
- reflex/components/core/client_side_routing.pyi +1 -1
- reflex/components/core/cond.py +9 -10
- reflex/components/core/debounce.py +1 -1
- reflex/components/core/foreach.py +23 -3
- reflex/components/core/html.py +1 -1
- reflex/components/core/match.py +5 -5
- reflex/components/core/sticky.py +160 -0
- reflex/components/core/sticky.pyi +449 -0
- reflex/components/core/upload.py +2 -2
- reflex/components/datadisplay/code.py +5 -14
- reflex/components/datadisplay/dataeditor.py +7 -4
- reflex/components/datadisplay/logo.py +13 -8
- reflex/components/datadisplay/shiki_code_block.py +14 -9
- reflex/components/dynamic.py +22 -3
- reflex/components/el/constants/reflex.py +1 -1
- reflex/components/el/element.py +1 -1
- reflex/components/el/elements/forms.py +4 -4
- reflex/components/el/elements/forms.pyi +4 -4
- reflex/components/lucide/icon.py +46 -8
- reflex/components/lucide/icon.pyi +54 -0
- reflex/components/markdown/markdown.py +10 -8
- reflex/components/moment/moment.py +2 -2
- reflex/components/next/image.py +16 -4
- reflex/components/next/image.pyi +4 -2
- reflex/components/next/link.py +1 -1
- reflex/components/plotly/plotly.py +5 -5
- reflex/components/props.py +3 -3
- reflex/components/radix/__init__.pyi +1 -1
- reflex/components/radix/primitives/accordion.py +9 -5
- reflex/components/radix/primitives/accordion.pyi +3 -1
- reflex/components/radix/primitives/drawer.py +5 -2
- reflex/components/radix/primitives/drawer.pyi +4 -4
- reflex/components/radix/primitives/form.pyi +6 -6
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/themes/color_mode.py +11 -9
- reflex/components/radix/themes/components/alert_dialog.py +3 -0
- reflex/components/radix/themes/components/card.py +1 -1
- reflex/components/radix/themes/components/card.pyi +1 -1
- reflex/components/radix/themes/components/context_menu.py +5 -0
- reflex/components/radix/themes/components/dialog.py +3 -0
- reflex/components/radix/themes/components/dropdown_menu.py +5 -0
- reflex/components/radix/themes/components/hover_card.py +3 -0
- reflex/components/radix/themes/components/icon_button.py +2 -2
- reflex/components/radix/themes/components/icon_button.pyi +1 -0
- reflex/components/radix/themes/components/popover.py +3 -0
- reflex/components/radix/themes/components/radio_cards.py +2 -0
- reflex/components/radix/themes/components/radio_group.py +1 -1
- reflex/components/radix/themes/components/select.py +3 -0
- reflex/components/radix/themes/components/tabs.py +3 -0
- reflex/components/radix/themes/components/text_area.py +12 -0
- reflex/components/radix/themes/components/text_area.pyi +2 -0
- reflex/components/radix/themes/components/text_field.py +1 -1
- reflex/components/radix/themes/components/tooltip.py +3 -1
- reflex/components/radix/themes/components/tooltip.pyi +1 -0
- reflex/components/radix/themes/layout/__init__.pyi +1 -1
- reflex/components/radix/themes/layout/list.py +2 -2
- reflex/components/radix/themes/layout/stack.py +2 -2
- reflex/components/radix/themes/typography/link.py +1 -1
- reflex/components/radix/themes/typography/text.py +2 -2
- reflex/components/react_player/react_player.py +1 -1
- reflex/components/recharts/__init__.py +2 -0
- reflex/components/recharts/__init__.pyi +2 -0
- reflex/components/recharts/charts.py +15 -15
- reflex/components/recharts/general.py +19 -4
- reflex/components/recharts/general.pyi +55 -4
- reflex/components/recharts/polar.py +2 -2
- reflex/components/recharts/recharts.py +4 -4
- reflex/components/sonner/toast.py +15 -13
- reflex/components/sonner/toast.pyi +6 -6
- reflex/components/suneditor/editor.py +6 -4
- reflex/components/suneditor/editor.pyi +2 -2
- reflex/components/tags/iter_tag.py +3 -3
- reflex/components/tags/tag.py +25 -3
- reflex/config.py +48 -15
- reflex/constants/__init__.py +1 -0
- reflex/constants/base.py +4 -1
- reflex/constants/compiler.py +5 -2
- reflex/constants/config.py +8 -1
- reflex/constants/installer.py +9 -9
- reflex/constants/style.py +1 -1
- reflex/custom_components/custom_components.py +9 -7
- reflex/event.py +130 -161
- reflex/experimental/__init__.py +19 -11
- reflex/experimental/client_state.py +53 -28
- reflex/experimental/hooks.py +5 -5
- reflex/experimental/layout.py +8 -5
- reflex/experimental/layout.pyi +1 -1
- reflex/experimental/misc.py +3 -3
- reflex/istate/wrappers.py +1 -1
- reflex/middleware/hydrate_middleware.py +2 -2
- reflex/model.py +11 -6
- reflex/page.py +3 -3
- reflex/reflex.py +90 -19
- reflex/route.py +1 -1
- reflex/state.py +358 -401
- reflex/style.py +27 -3
- reflex/testing.py +29 -23
- reflex/utils/build.py +6 -2
- reflex/utils/codespaces.py +1 -4
- reflex/utils/compat.py +6 -5
- reflex/utils/console.py +52 -16
- reflex/utils/exceptions.py +76 -26
- reflex/utils/exec.py +69 -74
- reflex/utils/export.py +6 -1
- reflex/utils/format.py +7 -39
- reflex/utils/imports.py +2 -2
- reflex/utils/lazy_loader.py +7 -1
- reflex/utils/path_ops.py +28 -14
- reflex/utils/prerequisites.py +324 -65
- reflex/utils/processes.py +45 -32
- reflex/utils/pyi_generator.py +30 -25
- reflex/utils/registry.py +4 -4
- reflex/utils/serializers.py +1 -1
- reflex/utils/telemetry.py +5 -4
- reflex/utils/types.py +42 -18
- reflex/vars/base.py +650 -333
- reflex/vars/datetime.py +6 -7
- reflex/vars/dep_tracking.py +344 -0
- reflex/vars/function.py +11 -5
- reflex/vars/number.py +31 -43
- reflex/vars/object.py +63 -62
- reflex/vars/sequence.py +79 -67
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/METADATA +7 -8
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/RECORD +153 -149
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/WHEEL +1 -1
- reflex/experimental/assets.py +0 -37
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/LICENSE +0 -0
- {reflex-0.6.8a2.dist-info → reflex-0.7.0a1.dist-info}/entry_points.txt +0 -0
reflex/experimental/__init__.py
CHANGED
|
@@ -9,21 +9,29 @@ from reflex.components.sonner.toast import toast as toast
|
|
|
9
9
|
|
|
10
10
|
from ..utils.console import warn
|
|
11
11
|
from . import hooks as hooks
|
|
12
|
-
from .assets import asset as asset
|
|
13
12
|
from .client_state import ClientStateVar as ClientStateVar
|
|
14
13
|
from .layout import layout as layout
|
|
15
14
|
from .misc import run_in_thread as run_in_thread
|
|
16
15
|
|
|
17
|
-
warn(
|
|
18
|
-
"`rx._x` contains experimental features and might be removed at any time in the future .",
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
_EMITTED_PROMOTION_WARNINGS = set()
|
|
22
|
-
|
|
23
16
|
|
|
24
17
|
class ExperimentalNamespace(SimpleNamespace):
|
|
25
18
|
"""Namespace for experimental features."""
|
|
26
19
|
|
|
20
|
+
def __getattribute__(self, item: str):
|
|
21
|
+
"""Get attribute from the namespace.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
item: attribute name.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
The attribute.
|
|
28
|
+
"""
|
|
29
|
+
warn(
|
|
30
|
+
"`rx._x` contains experimental features and might be removed at any time in the future.",
|
|
31
|
+
dedupe=True,
|
|
32
|
+
)
|
|
33
|
+
return super().__getattribute__(item)
|
|
34
|
+
|
|
27
35
|
@property
|
|
28
36
|
def toast(self):
|
|
29
37
|
"""Temporary property returning the toast namespace.
|
|
@@ -56,13 +64,13 @@ class ExperimentalNamespace(SimpleNamespace):
|
|
|
56
64
|
Args:
|
|
57
65
|
component_name: name of the component.
|
|
58
66
|
"""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
warn(
|
|
68
|
+
f"`rx._x.{component_name}` was promoted to `rx.{component_name}`.",
|
|
69
|
+
dedupe=True,
|
|
70
|
+
)
|
|
62
71
|
|
|
63
72
|
|
|
64
73
|
_x = ExperimentalNamespace(
|
|
65
|
-
asset=asset,
|
|
66
74
|
client_state=ClientStateVar.create,
|
|
67
75
|
hooks=hooks,
|
|
68
76
|
layout=layout,
|
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import dataclasses
|
|
6
6
|
import re
|
|
7
|
-
import sys
|
|
8
7
|
from typing import Any, Callable, Union
|
|
9
8
|
|
|
10
9
|
from reflex import constants
|
|
@@ -34,10 +33,22 @@ def _client_state_ref(var_name: str) -> str:
|
|
|
34
33
|
return f"refs['_client_state_{var_name}']"
|
|
35
34
|
|
|
36
35
|
|
|
36
|
+
def _client_state_ref_dict(var_name: str) -> str:
|
|
37
|
+
"""Get the ref path for a ClientStateVar.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
var_name: The name of the variable.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
An accessor for ClientStateVar ref as a string.
|
|
44
|
+
"""
|
|
45
|
+
return f"refs['_client_state_dict_{var_name}']"
|
|
46
|
+
|
|
47
|
+
|
|
37
48
|
@dataclasses.dataclass(
|
|
38
49
|
eq=False,
|
|
39
50
|
frozen=True,
|
|
40
|
-
|
|
51
|
+
slots=True,
|
|
41
52
|
)
|
|
42
53
|
class ClientStateVar(Var):
|
|
43
54
|
"""A Var that exists on the client via useState."""
|
|
@@ -115,10 +126,41 @@ class ClientStateVar(Var):
|
|
|
115
126
|
"react": [ImportVar(tag="useState"), ImportVar(tag="useId")],
|
|
116
127
|
}
|
|
117
128
|
if global_ref:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
129
|
+
arg_name = get_unique_variable_name()
|
|
130
|
+
func = ArgsFunctionOperationBuilder.create(
|
|
131
|
+
args_names=(arg_name,),
|
|
132
|
+
return_expr=Var("Array.prototype.forEach.call")
|
|
133
|
+
.to(FunctionVar)
|
|
134
|
+
.call(
|
|
135
|
+
(
|
|
136
|
+
Var("Object.values")
|
|
137
|
+
.to(FunctionVar)
|
|
138
|
+
.call(Var(_client_state_ref_dict(setter_name)))
|
|
139
|
+
.to(list)
|
|
140
|
+
.to(list)
|
|
141
|
+
)
|
|
142
|
+
+ Var.create(
|
|
143
|
+
[
|
|
144
|
+
Var(
|
|
145
|
+
f"(value) => {{ {_client_state_ref(var_name)} = value; }}"
|
|
146
|
+
)
|
|
147
|
+
]
|
|
148
|
+
).to(list),
|
|
149
|
+
ArgsFunctionOperationBuilder.create(
|
|
150
|
+
args_names=("setter",),
|
|
151
|
+
return_expr=Var("setter").to(FunctionVar).call(Var(arg_name)),
|
|
152
|
+
),
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
hooks[f"{_client_state_ref(setter_name)} = {func!s}"] = None
|
|
157
|
+
hooks[f"{_client_state_ref(var_name)} ??= {var_name!s}"] = None
|
|
158
|
+
hooks[f"{_client_state_ref_dict(var_name)} ??= {{}}"] = None
|
|
159
|
+
hooks[f"{_client_state_ref_dict(setter_name)} ??= {{}}"] = None
|
|
160
|
+
hooks[f"{_client_state_ref_dict(var_name)}[{id_name}] = {var_name}"] = None
|
|
161
|
+
hooks[
|
|
162
|
+
f"{_client_state_ref_dict(setter_name)}[{id_name}] = {setter_name}"
|
|
163
|
+
] = None
|
|
122
164
|
imports.update(_refs_import)
|
|
123
165
|
return cls(
|
|
124
166
|
_js_expr="",
|
|
@@ -150,7 +192,7 @@ class ClientStateVar(Var):
|
|
|
150
192
|
return (
|
|
151
193
|
Var(
|
|
152
194
|
_js_expr=(
|
|
153
|
-
|
|
195
|
+
_client_state_ref_dict(self._getter_name) + f"[{self._id_name}]"
|
|
154
196
|
if self._global_ref
|
|
155
197
|
else self._getter_name
|
|
156
198
|
),
|
|
@@ -158,9 +200,7 @@ class ClientStateVar(Var):
|
|
|
158
200
|
)
|
|
159
201
|
.to(self._var_type)
|
|
160
202
|
._replace(
|
|
161
|
-
merge_var_data=VarData(
|
|
162
|
-
imports=_refs_import if self._global_ref else {}
|
|
163
|
-
)
|
|
203
|
+
merge_var_data=VarData(imports=_refs_import if self._global_ref else {})
|
|
164
204
|
)
|
|
165
205
|
)
|
|
166
206
|
|
|
@@ -179,26 +219,11 @@ class ClientStateVar(Var):
|
|
|
179
219
|
"""
|
|
180
220
|
_var_data = VarData(imports=_refs_import if self._global_ref else {})
|
|
181
221
|
|
|
182
|
-
arg_name = get_unique_variable_name()
|
|
183
222
|
setter = (
|
|
184
|
-
|
|
185
|
-
args_names=(arg_name,),
|
|
186
|
-
return_expr=Var("Array.prototype.forEach.call")
|
|
187
|
-
.to(FunctionVar)
|
|
188
|
-
.call(
|
|
189
|
-
Var("Object.values")
|
|
190
|
-
.to(FunctionVar)
|
|
191
|
-
.call(Var(_client_state_ref(self._setter_name))),
|
|
192
|
-
ArgsFunctionOperationBuilder.create(
|
|
193
|
-
args_names=("setter",),
|
|
194
|
-
return_expr=Var("setter").to(FunctionVar).call(Var(arg_name)),
|
|
195
|
-
),
|
|
196
|
-
),
|
|
197
|
-
_var_data=_var_data,
|
|
198
|
-
)
|
|
223
|
+
Var(_client_state_ref(self._setter_name))
|
|
199
224
|
if self._global_ref
|
|
200
|
-
else Var(self._setter_name, _var_data=_var_data)
|
|
201
|
-
)
|
|
225
|
+
else Var(self._setter_name, _var_data=_var_data)
|
|
226
|
+
).to(FunctionVar)
|
|
202
227
|
|
|
203
228
|
if value is not NoValue:
|
|
204
229
|
# This is a hack to make it work like an EventSpec taking an arg
|
reflex/experimental/hooks.py
CHANGED
|
@@ -11,7 +11,7 @@ def _compose_react_imports(tags: list[str]) -> dict[str, list[ImportVar]]:
|
|
|
11
11
|
return {"react": [ImportVar(tag=tag) for tag in tags]}
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def const(name, value) -> Var:
|
|
14
|
+
def const(name: str | list[str], value: str | Var) -> Var:
|
|
15
15
|
"""Create a constant Var.
|
|
16
16
|
|
|
17
17
|
Args:
|
|
@@ -26,7 +26,7 @@ def const(name, value) -> Var:
|
|
|
26
26
|
return Var(_js_expr=f"const {name} = {value}")
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def useCallback(func, deps) -> Var:
|
|
29
|
+
def useCallback(func: str, deps: list) -> Var: # noqa: N802
|
|
30
30
|
"""Create a useCallback hook with a function and dependencies.
|
|
31
31
|
|
|
32
32
|
Args:
|
|
@@ -42,7 +42,7 @@ def useCallback(func, deps) -> Var:
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def useContext(context) -> Var:
|
|
45
|
+
def useContext(context: str) -> Var: # noqa: N802
|
|
46
46
|
"""Create a useContext hook with a context.
|
|
47
47
|
|
|
48
48
|
Args:
|
|
@@ -57,7 +57,7 @@ def useContext(context) -> Var:
|
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def useRef(default) -> Var:
|
|
60
|
+
def useRef(default: str) -> Var: # noqa: N802
|
|
61
61
|
"""Create a useRef hook with a default value.
|
|
62
62
|
|
|
63
63
|
Args:
|
|
@@ -72,7 +72,7 @@ def useRef(default) -> Var:
|
|
|
72
72
|
)
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def useState(var_name, default=None) -> Var:
|
|
75
|
+
def useState(var_name: str, default: str | None = None) -> Var: # noqa: N802
|
|
76
76
|
"""Create a useState hook with a variable name and setter name.
|
|
77
77
|
|
|
78
78
|
Args:
|
reflex/experimental/layout.py
CHANGED
|
@@ -12,6 +12,7 @@ from reflex.components.radix.themes.components.icon_button import IconButton
|
|
|
12
12
|
from reflex.components.radix.themes.layout.box import Box
|
|
13
13
|
from reflex.components.radix.themes.layout.container import Container
|
|
14
14
|
from reflex.components.radix.themes.layout.stack import HStack
|
|
15
|
+
from reflex.constants.compiler import MemoizationMode
|
|
15
16
|
from reflex.event import run_script
|
|
16
17
|
from reflex.experimental import hooks
|
|
17
18
|
from reflex.state import ComponentState
|
|
@@ -44,10 +45,10 @@ class Sidebar(Box, MemoizationLeaf):
|
|
|
44
45
|
Returns:
|
|
45
46
|
The style of the component.
|
|
46
47
|
"""
|
|
47
|
-
sidebar: Component = self.children[-2] #
|
|
48
|
-
spacer: Component = self.children[-1] #
|
|
48
|
+
sidebar: Component = self.children[-2] # pyright: ignore [reportAssignmentType]
|
|
49
|
+
spacer: Component = self.children[-1] # pyright: ignore [reportAssignmentType]
|
|
49
50
|
open = (
|
|
50
|
-
self.State.open #
|
|
51
|
+
self.State.open # pyright: ignore [reportAttributeAccessIssue]
|
|
51
52
|
if self.State
|
|
52
53
|
else Var(_js_expr="open")
|
|
53
54
|
)
|
|
@@ -146,6 +147,8 @@ sidebar_trigger_style = {
|
|
|
146
147
|
class SidebarTrigger(Fragment):
|
|
147
148
|
"""A component that renders the sidebar trigger."""
|
|
148
149
|
|
|
150
|
+
_memoization_mode = MemoizationMode(recursive=False)
|
|
151
|
+
|
|
149
152
|
@classmethod
|
|
150
153
|
def create(cls, sidebar: Component, **props):
|
|
151
154
|
"""Create the sidebar trigger component.
|
|
@@ -159,11 +162,11 @@ class SidebarTrigger(Fragment):
|
|
|
159
162
|
"""
|
|
160
163
|
trigger_props = {**props, **sidebar_trigger_style}
|
|
161
164
|
|
|
162
|
-
inner_sidebar: Component = sidebar.children[0] #
|
|
165
|
+
inner_sidebar: Component = sidebar.children[0] # pyright: ignore [reportAssignmentType]
|
|
163
166
|
sidebar_width = inner_sidebar.style.get("width")
|
|
164
167
|
|
|
165
168
|
if sidebar.State:
|
|
166
|
-
open, toggle = sidebar.State.open, sidebar.State.toggle #
|
|
169
|
+
open, toggle = sidebar.State.open, sidebar.State.toggle # pyright: ignore [reportAttributeAccessIssue]
|
|
167
170
|
else:
|
|
168
171
|
open, toggle = (
|
|
169
172
|
Var(_js_expr="open"),
|
reflex/experimental/layout.pyi
CHANGED
|
@@ -109,7 +109,7 @@ class DrawerSidebar(DrawerRoot):
|
|
|
109
109
|
snap_points: Optional[List[Union[float, str]]] = None,
|
|
110
110
|
fade_from_index: Optional[Union[Var[int], int]] = None,
|
|
111
111
|
scroll_lock_timeout: Optional[Union[Var[int], int]] = None,
|
|
112
|
-
|
|
112
|
+
prevent_scroll_restoration: Optional[Union[Var[bool], bool]] = None,
|
|
113
113
|
should_scale_background: Optional[Union[Var[bool], bool]] = None,
|
|
114
114
|
close_threshold: Optional[Union[Var[float], float]] = None,
|
|
115
115
|
as_child: Optional[Union[Var[bool], bool]] = None,
|
reflex/experimental/misc.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"""Miscellaneous functions for the experimental package."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any, Callable
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
async def run_in_thread(func) -> Any:
|
|
7
|
+
async def run_in_thread(func: Callable) -> Any:
|
|
8
8
|
"""Run a function in a separate thread.
|
|
9
9
|
|
|
10
10
|
To not block the UI event queue, run_in_thread must be inside inside a rx.event(background=True) decorated method.
|
|
11
11
|
|
|
12
12
|
Args:
|
|
13
|
-
func
|
|
13
|
+
func: The non-async function to run.
|
|
14
14
|
|
|
15
15
|
Raises:
|
|
16
16
|
ValueError: If the function is an async function.
|
reflex/istate/wrappers.py
CHANGED
|
@@ -6,7 +6,7 @@ from reflex.istate.proxy import ReadOnlyStateProxy
|
|
|
6
6
|
from reflex.state import _split_substate_key, _substate_key, get_state_manager
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
async def get_state(token, state_cls: Any | None = None) -> ReadOnlyStateProxy:
|
|
9
|
+
async def get_state(token: str, state_cls: Any | None = None) -> ReadOnlyStateProxy:
|
|
10
10
|
"""Get the instance of a state for a token.
|
|
11
11
|
|
|
12
12
|
Args:
|
|
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Optional
|
|
|
8
8
|
from reflex import constants
|
|
9
9
|
from reflex.event import Event, get_hydrate_event
|
|
10
10
|
from reflex.middleware.middleware import Middleware
|
|
11
|
-
from reflex.state import BaseState, StateUpdate
|
|
11
|
+
from reflex.state import BaseState, StateUpdate, _resolve_delta
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from reflex.app import App
|
|
@@ -42,7 +42,7 @@ class HydrateMiddleware(Middleware):
|
|
|
42
42
|
setattr(state, constants.CompileVars.IS_HYDRATED, False)
|
|
43
43
|
|
|
44
44
|
# Get the initial state.
|
|
45
|
-
delta = state.dict()
|
|
45
|
+
delta = await _resolve_delta(state.dict())
|
|
46
46
|
# since a full dict was captured, clean any dirtiness
|
|
47
47
|
state._clean()
|
|
48
48
|
|
reflex/model.py
CHANGED
|
@@ -18,6 +18,7 @@ import sqlalchemy
|
|
|
18
18
|
import sqlalchemy.exc
|
|
19
19
|
import sqlalchemy.ext.asyncio
|
|
20
20
|
import sqlalchemy.orm
|
|
21
|
+
from alembic.runtime.migration import MigrationContext
|
|
21
22
|
|
|
22
23
|
from reflex.base import Base
|
|
23
24
|
from reflex.config import environment, get_config
|
|
@@ -242,7 +243,7 @@ class ModelRegistry:
|
|
|
242
243
|
return metadata
|
|
243
244
|
|
|
244
245
|
|
|
245
|
-
class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssues]
|
|
246
|
+
class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssues,reportIncompatibleVariableOverride]
|
|
246
247
|
"""Base class to define a table in the database."""
|
|
247
248
|
|
|
248
249
|
# The primary key for the table.
|
|
@@ -261,7 +262,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
|
|
|
261
262
|
super().__init_subclass__()
|
|
262
263
|
|
|
263
264
|
@classmethod
|
|
264
|
-
def _dict_recursive(cls, value):
|
|
265
|
+
def _dict_recursive(cls, value: Any):
|
|
265
266
|
"""Recursively serialize the relationship object(s).
|
|
266
267
|
|
|
267
268
|
Args:
|
|
@@ -393,7 +394,11 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
|
|
|
393
394
|
writer = alembic.autogenerate.rewriter.Rewriter()
|
|
394
395
|
|
|
395
396
|
@writer.rewrites(alembic.operations.ops.AddColumnOp)
|
|
396
|
-
def render_add_column_with_server_default(
|
|
397
|
+
def render_add_column_with_server_default(
|
|
398
|
+
context: MigrationContext,
|
|
399
|
+
revision: str | None,
|
|
400
|
+
op: Any,
|
|
401
|
+
):
|
|
397
402
|
# Carry the sqlmodel default as server_default so that newly added
|
|
398
403
|
# columns get the desired default value in existing rows.
|
|
399
404
|
if op.column.default is not None and op.column.server_default is None:
|
|
@@ -402,7 +407,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
|
|
|
402
407
|
)
|
|
403
408
|
return op
|
|
404
409
|
|
|
405
|
-
def run_autogenerate(rev, context):
|
|
410
|
+
def run_autogenerate(rev: str, context: MigrationContext):
|
|
406
411
|
revision_context.run_autogenerate(rev, context)
|
|
407
412
|
return []
|
|
408
413
|
|
|
@@ -415,7 +420,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
|
|
|
415
420
|
connection=connection,
|
|
416
421
|
target_metadata=ModelRegistry.get_metadata(),
|
|
417
422
|
render_item=cls._alembic_render_item,
|
|
418
|
-
process_revision_directives=writer,
|
|
423
|
+
process_revision_directives=writer,
|
|
419
424
|
compare_type=False,
|
|
420
425
|
render_as_batch=True, # for sqlite compatibility
|
|
421
426
|
)
|
|
@@ -444,7 +449,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
|
|
|
444
449
|
"""
|
|
445
450
|
config, script_directory = cls._alembic_config()
|
|
446
451
|
|
|
447
|
-
def run_upgrade(rev, context):
|
|
452
|
+
def run_upgrade(rev: str, context: MigrationContext):
|
|
448
453
|
return script_directory._upgrade_revs(to_rev, rev)
|
|
449
454
|
|
|
450
455
|
with alembic.runtime.environment.EnvironmentContext(
|
reflex/page.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from collections import defaultdict
|
|
6
|
-
from typing import Any, Dict, List
|
|
6
|
+
from typing import Any, Callable, Dict, List
|
|
7
7
|
|
|
8
8
|
from reflex.config import get_config
|
|
9
9
|
from reflex.event import BASE_STATE, EventType
|
|
@@ -42,7 +42,7 @@ def page(
|
|
|
42
42
|
The decorated function.
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
|
-
def decorator(render_fn):
|
|
45
|
+
def decorator(render_fn: Callable):
|
|
46
46
|
kwargs = {}
|
|
47
47
|
if route:
|
|
48
48
|
kwargs["route"] = route
|
|
@@ -66,7 +66,7 @@ def page(
|
|
|
66
66
|
return decorator
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def get_decorated_pages(omit_implicit_routes=True) -> list[dict[str, Any]]:
|
|
69
|
+
def get_decorated_pages(omit_implicit_routes: bool = True) -> list[dict[str, Any]]:
|
|
70
70
|
"""Get the decorated pages.
|
|
71
71
|
|
|
72
72
|
Args:
|
reflex/reflex.py
CHANGED
|
@@ -17,7 +17,7 @@ from reflex.state import reset_disk_state_manager
|
|
|
17
17
|
from reflex.utils import console, telemetry
|
|
18
18
|
|
|
19
19
|
# Disable typer+rich integration for help panels
|
|
20
|
-
typer.core.rich = None #
|
|
20
|
+
typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
|
|
21
21
|
|
|
22
22
|
# Create the app.
|
|
23
23
|
try:
|
|
@@ -26,6 +26,8 @@ except TypeError:
|
|
|
26
26
|
# Fallback for older typer versions.
|
|
27
27
|
cli = typer.Typer(add_completion=False)
|
|
28
28
|
|
|
29
|
+
SHOW_BUILT_WITH_REFLEX_INFO = "https://reflex.dev/docs/hosting/reflex-branding/"
|
|
30
|
+
|
|
29
31
|
# Get the config.
|
|
30
32
|
config = get_config()
|
|
31
33
|
|
|
@@ -125,8 +127,8 @@ def _run(
|
|
|
125
127
|
env: constants.Env = constants.Env.DEV,
|
|
126
128
|
frontend: bool = True,
|
|
127
129
|
backend: bool = True,
|
|
128
|
-
frontend_port:
|
|
129
|
-
backend_port:
|
|
130
|
+
frontend_port: int = config.frontend_port,
|
|
131
|
+
backend_port: int = config.backend_port,
|
|
130
132
|
backend_host: str = config.backend_host,
|
|
131
133
|
loglevel: constants.LogLevel = config.loglevel,
|
|
132
134
|
):
|
|
@@ -160,18 +162,22 @@ def _run(
|
|
|
160
162
|
# Find the next available open port if applicable.
|
|
161
163
|
if frontend:
|
|
162
164
|
frontend_port = processes.handle_port(
|
|
163
|
-
"frontend",
|
|
165
|
+
"frontend",
|
|
166
|
+
frontend_port,
|
|
167
|
+
constants.DefaultPorts.FRONTEND_PORT,
|
|
164
168
|
)
|
|
165
169
|
|
|
166
170
|
if backend:
|
|
167
171
|
backend_port = processes.handle_port(
|
|
168
|
-
"backend",
|
|
172
|
+
"backend",
|
|
173
|
+
backend_port,
|
|
174
|
+
constants.DefaultPorts.BACKEND_PORT,
|
|
169
175
|
)
|
|
170
176
|
|
|
171
177
|
# Apply the new ports to the config.
|
|
172
|
-
if frontend_port !=
|
|
178
|
+
if frontend_port != config.frontend_port:
|
|
173
179
|
config._set_persistent(frontend_port=frontend_port)
|
|
174
|
-
if backend_port !=
|
|
180
|
+
if backend_port != config.backend_port:
|
|
175
181
|
config._set_persistent(backend_port=backend_port)
|
|
176
182
|
|
|
177
183
|
# Reload the config to make sure the env vars are persistent.
|
|
@@ -182,6 +188,15 @@ def _run(
|
|
|
182
188
|
prerequisites.check_latest_package_version(constants.Reflex.MODULE_NAME)
|
|
183
189
|
|
|
184
190
|
if frontend:
|
|
191
|
+
if not config.show_built_with_reflex:
|
|
192
|
+
# The sticky badge may be disabled at runtime for team/enterprise tiers.
|
|
193
|
+
prerequisites.check_config_option_in_tier(
|
|
194
|
+
option_name="show_built_with_reflex",
|
|
195
|
+
allowed_tiers=["team", "enterprise"],
|
|
196
|
+
fallback_value=True,
|
|
197
|
+
help_link=SHOW_BUILT_WITH_REFLEX_INFO,
|
|
198
|
+
)
|
|
199
|
+
|
|
185
200
|
# Get the app module.
|
|
186
201
|
prerequisites.get_compiled_app()
|
|
187
202
|
|
|
@@ -262,10 +277,10 @@ def run(
|
|
|
262
277
|
help="Execute only backend.",
|
|
263
278
|
envvar=environment.REFLEX_BACKEND_ONLY.name,
|
|
264
279
|
),
|
|
265
|
-
frontend_port:
|
|
280
|
+
frontend_port: int = typer.Option(
|
|
266
281
|
config.frontend_port, help="Specify a different frontend port."
|
|
267
282
|
),
|
|
268
|
-
backend_port:
|
|
283
|
+
backend_port: int = typer.Option(
|
|
269
284
|
config.backend_port, help="Specify a different backend port."
|
|
270
285
|
),
|
|
271
286
|
backend_host: str = typer.Option(
|
|
@@ -306,6 +321,9 @@ def export(
|
|
|
306
321
|
help="Whether to exclude sqlite db files when exporting backend.",
|
|
307
322
|
hidden=True,
|
|
308
323
|
),
|
|
324
|
+
env: constants.Env = typer.Option(
|
|
325
|
+
constants.Env.PROD, help="The environment to export the app in."
|
|
326
|
+
),
|
|
309
327
|
loglevel: constants.LogLevel = typer.Option(
|
|
310
328
|
config.loglevel, help="The log level to use."
|
|
311
329
|
),
|
|
@@ -317,12 +335,22 @@ def export(
|
|
|
317
335
|
if prerequisites.needs_reinit(frontend=True):
|
|
318
336
|
_init(name=config.app_name, loglevel=loglevel)
|
|
319
337
|
|
|
338
|
+
if frontend and not config.show_built_with_reflex:
|
|
339
|
+
# The sticky badge may be disabled on export for team/enterprise tiers.
|
|
340
|
+
prerequisites.check_config_option_in_tier(
|
|
341
|
+
option_name="show_built_with_reflex",
|
|
342
|
+
allowed_tiers=["team", "enterprise"],
|
|
343
|
+
fallback_value=False,
|
|
344
|
+
help_link=SHOW_BUILT_WITH_REFLEX_INFO,
|
|
345
|
+
)
|
|
346
|
+
|
|
320
347
|
export_utils.export(
|
|
321
348
|
zipping=zipping,
|
|
322
349
|
frontend=frontend,
|
|
323
350
|
backend=backend,
|
|
324
351
|
zip_dest_dir=zip_dest_dir,
|
|
325
352
|
upload_db_file=upload_db_file,
|
|
353
|
+
env=env,
|
|
326
354
|
loglevel=loglevel.subprocess_level(),
|
|
327
355
|
)
|
|
328
356
|
|
|
@@ -351,7 +379,7 @@ def logout(
|
|
|
351
379
|
|
|
352
380
|
check_version()
|
|
353
381
|
|
|
354
|
-
logout(loglevel) #
|
|
382
|
+
logout(loglevel) # pyright: ignore [reportArgumentType]
|
|
355
383
|
|
|
356
384
|
|
|
357
385
|
db_cli = typer.Typer()
|
|
@@ -440,7 +468,11 @@ def deploy(
|
|
|
440
468
|
config.app_name,
|
|
441
469
|
"--app-name",
|
|
442
470
|
help="The name of the App to deploy under.",
|
|
443
|
-
|
|
471
|
+
),
|
|
472
|
+
app_id: str = typer.Option(
|
|
473
|
+
None,
|
|
474
|
+
"--app-id",
|
|
475
|
+
help="The ID of the App to deploy over.",
|
|
444
476
|
),
|
|
445
477
|
regions: List[str] = typer.Option(
|
|
446
478
|
[],
|
|
@@ -480,6 +512,11 @@ def deploy(
|
|
|
480
512
|
"--project",
|
|
481
513
|
help="project id to deploy to",
|
|
482
514
|
),
|
|
515
|
+
project_name: Optional[str] = typer.Option(
|
|
516
|
+
None,
|
|
517
|
+
"--project-name",
|
|
518
|
+
help="The name of the project to deploy to.",
|
|
519
|
+
),
|
|
483
520
|
token: Optional[str] = typer.Option(
|
|
484
521
|
None,
|
|
485
522
|
"--token",
|
|
@@ -492,6 +529,7 @@ def deploy(
|
|
|
492
529
|
),
|
|
493
530
|
):
|
|
494
531
|
"""Deploy the app to the Reflex hosting service."""
|
|
532
|
+
from reflex_cli.constants.base import LogLevel as HostingLogLevel
|
|
495
533
|
from reflex_cli.utils import dependency
|
|
496
534
|
from reflex_cli.v2 import cli as hosting_cli
|
|
497
535
|
|
|
@@ -500,15 +538,32 @@ def deploy(
|
|
|
500
538
|
|
|
501
539
|
check_version()
|
|
502
540
|
|
|
541
|
+
if not config.show_built_with_reflex:
|
|
542
|
+
# The sticky badge may be disabled on deploy for pro/team/enterprise tiers.
|
|
543
|
+
prerequisites.check_config_option_in_tier(
|
|
544
|
+
option_name="show_built_with_reflex",
|
|
545
|
+
allowed_tiers=["pro", "team", "enterprise"],
|
|
546
|
+
fallback_value=True,
|
|
547
|
+
help_link=SHOW_BUILT_WITH_REFLEX_INFO,
|
|
548
|
+
)
|
|
549
|
+
|
|
503
550
|
# Set the log level.
|
|
504
551
|
console.set_log_level(loglevel)
|
|
505
552
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
553
|
+
def convert_reflex_loglevel_to_reflex_cli_loglevel(
|
|
554
|
+
loglevel: constants.LogLevel,
|
|
555
|
+
) -> HostingLogLevel:
|
|
556
|
+
if loglevel == constants.LogLevel.DEBUG:
|
|
557
|
+
return HostingLogLevel.DEBUG
|
|
558
|
+
if loglevel == constants.LogLevel.INFO:
|
|
559
|
+
return HostingLogLevel.INFO
|
|
560
|
+
if loglevel == constants.LogLevel.WARNING:
|
|
561
|
+
return HostingLogLevel.WARNING
|
|
562
|
+
if loglevel == constants.LogLevel.ERROR:
|
|
563
|
+
return HostingLogLevel.ERROR
|
|
564
|
+
if loglevel == constants.LogLevel.CRITICAL:
|
|
565
|
+
return HostingLogLevel.CRITICAL
|
|
566
|
+
return HostingLogLevel.INFO
|
|
512
567
|
|
|
513
568
|
# Only check requirements if interactive.
|
|
514
569
|
# There is user interaction for requirements update.
|
|
@@ -522,6 +577,7 @@ def deploy(
|
|
|
522
577
|
|
|
523
578
|
hosting_cli.deploy(
|
|
524
579
|
app_name=app_name,
|
|
580
|
+
app_id=app_id,
|
|
525
581
|
export_fn=lambda zip_dest_dir,
|
|
526
582
|
api_url,
|
|
527
583
|
deploy_url,
|
|
@@ -542,13 +598,28 @@ def deploy(
|
|
|
542
598
|
envfile=envfile,
|
|
543
599
|
hostname=hostname,
|
|
544
600
|
interactive=interactive,
|
|
545
|
-
loglevel=
|
|
601
|
+
loglevel=convert_reflex_loglevel_to_reflex_cli_loglevel(loglevel),
|
|
546
602
|
token=token,
|
|
547
603
|
project=project,
|
|
548
|
-
|
|
604
|
+
project_name=project_name,
|
|
605
|
+
**({"config_path": config_path} if config_path is not None else {}),
|
|
549
606
|
)
|
|
550
607
|
|
|
551
608
|
|
|
609
|
+
@cli.command()
|
|
610
|
+
def rename(
|
|
611
|
+
new_name: str = typer.Argument(..., help="The new name for the app."),
|
|
612
|
+
loglevel: constants.LogLevel = typer.Option(
|
|
613
|
+
config.loglevel, help="The log level to use."
|
|
614
|
+
),
|
|
615
|
+
):
|
|
616
|
+
"""Rename the app in the current directory."""
|
|
617
|
+
from reflex.utils import prerequisites
|
|
618
|
+
|
|
619
|
+
prerequisites.validate_app_name(new_name)
|
|
620
|
+
prerequisites.rename_app(new_name, loglevel)
|
|
621
|
+
|
|
622
|
+
|
|
552
623
|
cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.")
|
|
553
624
|
cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.")
|
|
554
625
|
cli.add_typer(
|
reflex/route.py
CHANGED
|
@@ -103,7 +103,7 @@ def catchall_prefix(route: str) -> str:
|
|
|
103
103
|
return route.replace(pattern, "") if pattern else ""
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def replace_brackets_with_keywords(input_string):
|
|
106
|
+
def replace_brackets_with_keywords(input_string: str) -> str:
|
|
107
107
|
"""Replace brackets and everything inside it in a string with a keyword.
|
|
108
108
|
|
|
109
109
|
Args:
|