reflex 0.6.3.post1__py3-none-any.whl → 0.6.4__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/_app.js.jinja2 +2 -2
- reflex/.templates/jinja/web/utils/context.js.jinja2 +3 -1
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +3 -3
- reflex/.templates/web/components/shiki/code.js +29 -0
- reflex/.templates/web/jsconfig.json +2 -1
- reflex/.templates/web/utils/state.js +6 -4
- reflex/__init__.py +6 -3
- reflex/__init__.pyi +4 -3
- reflex/app.py +6 -5
- reflex/compiler/compiler.py +6 -7
- reflex/compiler/utils.py +8 -1
- reflex/components/base/error_boundary.py +37 -24
- reflex/components/base/error_boundary.pyi +8 -7
- reflex/components/component.py +9 -4
- reflex/components/core/banner.py +2 -2
- reflex/components/core/client_side_routing.py +1 -1
- reflex/components/core/clipboard.py +1 -1
- reflex/components/core/clipboard.pyi +1 -1
- reflex/components/core/cond.py +1 -1
- reflex/components/core/debounce.py +5 -1
- reflex/components/core/upload.py +7 -9
- reflex/components/core/upload.pyi +2 -2
- reflex/components/datadisplay/code.py +1 -1
- reflex/components/datadisplay/dataeditor.py +83 -18
- reflex/components/datadisplay/dataeditor.pyi +67 -15
- reflex/components/datadisplay/shiki_code_block.py +813 -0
- reflex/components/datadisplay/shiki_code_block.pyi +2211 -0
- reflex/components/dynamic.py +3 -3
- reflex/components/el/elements/forms.py +37 -23
- reflex/components/el/elements/forms.pyi +7 -4
- reflex/components/markdown/markdown.py +12 -1
- reflex/components/moment/moment.pyi +1 -1
- reflex/components/radix/primitives/drawer.pyi +2 -2
- reflex/components/radix/themes/base.py +2 -2
- reflex/components/radix/themes/color_mode.pyi +1 -1
- reflex/components/radix/themes/components/alert_dialog.pyi +1 -1
- reflex/components/radix/themes/components/checkbox.pyi +3 -3
- reflex/components/radix/themes/components/context_menu.pyi +1 -1
- reflex/components/radix/themes/components/dialog.pyi +2 -2
- reflex/components/radix/themes/components/dropdown_menu.pyi +2 -2
- reflex/components/radix/themes/components/hover_card.pyi +2 -2
- reflex/components/radix/themes/components/popover.pyi +1 -1
- reflex/components/radix/themes/components/radio_cards.pyi +1 -1
- reflex/components/radix/themes/components/radio_group.pyi +1 -1
- reflex/components/radix/themes/components/select.pyi +6 -6
- reflex/components/radix/themes/components/switch.pyi +1 -1
- reflex/components/radix/themes/components/tabs.pyi +2 -2
- reflex/components/radix/themes/components/tooltip.pyi +4 -2
- reflex/components/react_player/__init__.py +1 -0
- reflex/components/react_player/audio.pyi +6 -3
- reflex/components/react_player/react_player.py +12 -1
- reflex/components/react_player/react_player.pyi +11 -3
- reflex/components/react_player/video.pyi +6 -3
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/sonner/toast.py +1 -1
- reflex/components/suneditor/editor.py +40 -16
- reflex/components/suneditor/editor.pyi +15 -11
- reflex/config.py +284 -20
- reflex/constants/__init__.py +2 -0
- reflex/constants/base.py +53 -31
- reflex/constants/compiler.py +2 -12
- reflex/constants/config.py +1 -2
- reflex/constants/installer.py +88 -32
- reflex/constants/style.py +1 -1
- reflex/constants/utils.py +32 -0
- reflex/custom_components/custom_components.py +3 -3
- reflex/event.py +152 -84
- reflex/experimental/__init__.py +2 -0
- reflex/experimental/client_state.py +1 -1
- reflex/experimental/layout.pyi +1 -1
- reflex/istate/storage.py +144 -0
- reflex/model.py +8 -11
- reflex/reflex.py +18 -17
- reflex/state.py +83 -149
- reflex/style.py +1 -1
- reflex/testing.py +2 -1
- reflex/utils/build.py +0 -12
- reflex/utils/exceptions.py +8 -0
- reflex/utils/exec.py +22 -4
- reflex/utils/imports.py +6 -0
- reflex/utils/net.py +2 -4
- reflex/utils/path_ops.py +7 -21
- reflex/utils/prerequisites.py +11 -17
- reflex/utils/pyi_generator.py +91 -3
- reflex/utils/registry.py +2 -6
- reflex/utils/types.py +14 -0
- reflex/vars/base.py +453 -424
- reflex/vars/function.py +6 -16
- reflex/vars/number.py +46 -67
- reflex/vars/object.py +1 -31
- reflex/vars/sequence.py +177 -47
- {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/METADATA +1 -1
- {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/RECORD +96 -91
- {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/LICENSE +0 -0
- {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/WHEEL +0 -0
- {reflex-0.6.3.post1.dist-info → reflex-0.6.4.dist-info}/entry_points.txt +0 -0
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import enum
|
|
6
|
-
from typing import Dict, List, Literal, Optional, Union
|
|
6
|
+
from typing import Dict, List, Literal, Optional, Tuple, Union
|
|
7
7
|
|
|
8
8
|
from reflex.base import Base
|
|
9
9
|
from reflex.components.component import Component, NoSSRComponent
|
|
10
|
-
from reflex.event import EventHandler
|
|
10
|
+
from reflex.event import EventHandler, empty_event, identity_event
|
|
11
11
|
from reflex.utils.format import to_camel_case
|
|
12
12
|
from reflex.utils.imports import ImportDict, ImportVar
|
|
13
13
|
from reflex.vars.base import Var
|
|
@@ -68,6 +68,35 @@ class EditorOptions(Base):
|
|
|
68
68
|
button_list: Optional[List[Union[List[str], str]]]
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
def on_blur_spec(e: Var, content: Var[str]) -> Tuple[Var[str]]:
|
|
72
|
+
"""A helper function to specify the on_blur event handler.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
e: The event.
|
|
76
|
+
content: The content of the editor.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
A tuple containing the content of the editor.
|
|
80
|
+
"""
|
|
81
|
+
return (content,)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def on_paste_spec(
|
|
85
|
+
e: Var, clean_data: Var[str], max_char_count: Var[bool]
|
|
86
|
+
) -> Tuple[Var[str], Var[bool]]:
|
|
87
|
+
"""A helper function to specify the on_paste event handler.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
e: The event.
|
|
91
|
+
clean_data: The clean data.
|
|
92
|
+
max_char_count: The maximum character count.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
A tuple containing the clean data and the maximum character count.
|
|
96
|
+
"""
|
|
97
|
+
return (clean_data, max_char_count)
|
|
98
|
+
|
|
99
|
+
|
|
71
100
|
class Editor(NoSSRComponent):
|
|
72
101
|
"""A Rich Text Editor component based on SunEditor.
|
|
73
102
|
Not every JS prop is listed here (some are not easily usable from python),
|
|
@@ -178,36 +207,31 @@ class Editor(NoSSRComponent):
|
|
|
178
207
|
disable_toolbar: Var[bool]
|
|
179
208
|
|
|
180
209
|
# Fired when the editor content changes.
|
|
181
|
-
on_change: EventHandler[
|
|
210
|
+
on_change: EventHandler[identity_event(str)]
|
|
182
211
|
|
|
183
212
|
# Fired when the something is inputted in the editor.
|
|
184
|
-
on_input: EventHandler[
|
|
213
|
+
on_input: EventHandler[empty_event]
|
|
185
214
|
|
|
186
215
|
# Fired when the editor loses focus.
|
|
187
|
-
on_blur: EventHandler[
|
|
216
|
+
on_blur: EventHandler[on_blur_spec]
|
|
188
217
|
|
|
189
218
|
# Fired when the editor is loaded.
|
|
190
|
-
on_load: EventHandler[
|
|
191
|
-
|
|
192
|
-
# Fired when the editor is resized.
|
|
193
|
-
on_resize_editor: EventHandler[lambda height, prev_height: [height, prev_height]]
|
|
219
|
+
on_load: EventHandler[identity_event(bool)]
|
|
194
220
|
|
|
195
221
|
# Fired when the editor content is copied.
|
|
196
|
-
on_copy: EventHandler[
|
|
222
|
+
on_copy: EventHandler[empty_event]
|
|
197
223
|
|
|
198
224
|
# Fired when the editor content is cut.
|
|
199
|
-
on_cut: EventHandler[
|
|
225
|
+
on_cut: EventHandler[empty_event]
|
|
200
226
|
|
|
201
227
|
# Fired when the editor content is pasted.
|
|
202
|
-
on_paste: EventHandler[
|
|
203
|
-
lambda e, clean_data, max_char_count: [clean_data, max_char_count]
|
|
204
|
-
]
|
|
228
|
+
on_paste: EventHandler[on_paste_spec]
|
|
205
229
|
|
|
206
230
|
# Fired when the code view is toggled.
|
|
207
|
-
toggle_code_view: EventHandler[
|
|
231
|
+
toggle_code_view: EventHandler[identity_event(bool)]
|
|
208
232
|
|
|
209
233
|
# Fired when the full screen mode is toggled.
|
|
210
|
-
toggle_full_screen: EventHandler[
|
|
234
|
+
toggle_full_screen: EventHandler[identity_event(bool)]
|
|
211
235
|
|
|
212
236
|
def add_imports(self) -> ImportDict:
|
|
213
237
|
"""Add imports for the Editor component.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
5
5
|
# ------------------------------------------------------
|
|
6
6
|
import enum
|
|
7
|
-
from typing import Any, Dict, List, Literal, Optional, Union, overload
|
|
7
|
+
from typing import Any, Dict, List, Literal, Optional, Tuple, Union, overload
|
|
8
8
|
|
|
9
9
|
from reflex.base import Base
|
|
10
10
|
from reflex.components.component import NoSSRComponent
|
|
@@ -44,6 +44,11 @@ class EditorOptions(Base):
|
|
|
44
44
|
rtl: Optional[bool]
|
|
45
45
|
button_list: Optional[List[Union[List[str], str]]]
|
|
46
46
|
|
|
47
|
+
def on_blur_spec(e: Var, content: Var[str]) -> Tuple[Var[str]]: ...
|
|
48
|
+
def on_paste_spec(
|
|
49
|
+
e: Var, clean_data: Var[str], max_char_count: Var[bool]
|
|
50
|
+
) -> Tuple[Var[str], Var[bool]]: ...
|
|
51
|
+
|
|
47
52
|
class Editor(NoSSRComponent):
|
|
48
53
|
def add_imports(self) -> ImportDict: ...
|
|
49
54
|
@overload
|
|
@@ -122,16 +127,16 @@ class Editor(NoSSRComponent):
|
|
|
122
127
|
class_name: Optional[Any] = None,
|
|
123
128
|
autofocus: Optional[bool] = None,
|
|
124
129
|
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
|
125
|
-
on_blur: Optional[EventType] = None,
|
|
126
|
-
on_change: Optional[EventType] = None,
|
|
130
|
+
on_blur: Optional[EventType[str]] = None,
|
|
131
|
+
on_change: Optional[EventType[str]] = None,
|
|
127
132
|
on_click: Optional[EventType[[]]] = None,
|
|
128
133
|
on_context_menu: Optional[EventType[[]]] = None,
|
|
129
|
-
on_copy: Optional[EventType] = None,
|
|
130
|
-
on_cut: Optional[EventType] = None,
|
|
134
|
+
on_copy: Optional[EventType[[]]] = None,
|
|
135
|
+
on_cut: Optional[EventType[[]]] = None,
|
|
131
136
|
on_double_click: Optional[EventType[[]]] = None,
|
|
132
137
|
on_focus: Optional[EventType[[]]] = None,
|
|
133
|
-
on_input: Optional[EventType] = None,
|
|
134
|
-
on_load: Optional[EventType] = None,
|
|
138
|
+
on_input: Optional[EventType[[]]] = None,
|
|
139
|
+
on_load: Optional[EventType[bool]] = None,
|
|
135
140
|
on_mount: Optional[EventType[[]]] = None,
|
|
136
141
|
on_mouse_down: Optional[EventType[[]]] = None,
|
|
137
142
|
on_mouse_enter: Optional[EventType[[]]] = None,
|
|
@@ -140,12 +145,11 @@ class Editor(NoSSRComponent):
|
|
|
140
145
|
on_mouse_out: Optional[EventType[[]]] = None,
|
|
141
146
|
on_mouse_over: Optional[EventType[[]]] = None,
|
|
142
147
|
on_mouse_up: Optional[EventType[[]]] = None,
|
|
143
|
-
on_paste: Optional[EventType] = None,
|
|
144
|
-
on_resize_editor: Optional[EventType] = None,
|
|
148
|
+
on_paste: Optional[EventType[str, bool]] = None,
|
|
145
149
|
on_scroll: Optional[EventType[[]]] = None,
|
|
146
150
|
on_unmount: Optional[EventType[[]]] = None,
|
|
147
|
-
toggle_code_view: Optional[EventType] = None,
|
|
148
|
-
toggle_full_screen: Optional[EventType] = None,
|
|
151
|
+
toggle_code_view: Optional[EventType[bool]] = None,
|
|
152
|
+
toggle_full_screen: Optional[EventType[bool]] = None,
|
|
149
153
|
**props,
|
|
150
154
|
) -> "Editor":
|
|
151
155
|
"""Create an instance of Editor. No children allowed.
|
reflex/config.py
CHANGED
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import dataclasses
|
|
6
|
+
import enum
|
|
5
7
|
import importlib
|
|
8
|
+
import inspect
|
|
6
9
|
import os
|
|
7
10
|
import sys
|
|
8
11
|
import urllib.parse
|
|
9
12
|
from pathlib import Path
|
|
10
|
-
from typing import Any, Dict, List, Optional, Set
|
|
13
|
+
from typing import Any, Dict, List, Optional, Set
|
|
11
14
|
|
|
12
|
-
from
|
|
15
|
+
from typing_extensions import Annotated, get_type_hints
|
|
16
|
+
|
|
17
|
+
from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
|
|
18
|
+
from reflex.utils.types import GenericType, is_union, value_inside_optional
|
|
13
19
|
|
|
14
20
|
try:
|
|
15
21
|
import pydantic.v1 as pydantic
|
|
@@ -131,6 +137,258 @@ class DBConfig(Base):
|
|
|
131
137
|
return f"{self.engine}://{path}/{self.database}"
|
|
132
138
|
|
|
133
139
|
|
|
140
|
+
def get_default_value_for_field(field: dataclasses.Field) -> Any:
|
|
141
|
+
"""Get the default value for a field.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
field: The field.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
The default value.
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
ValueError: If no default value is found.
|
|
151
|
+
"""
|
|
152
|
+
if field.default != dataclasses.MISSING:
|
|
153
|
+
return field.default
|
|
154
|
+
elif field.default_factory != dataclasses.MISSING:
|
|
155
|
+
return field.default_factory()
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError(
|
|
158
|
+
f"Missing value for environment variable {field.name} and no default value found"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# TODO: Change all interpret_.* signatures to value: str, field: dataclasses.Field once we migrate rx.Config to dataclasses
|
|
163
|
+
def interpret_boolean_env(value: str, field_name: str) -> bool:
|
|
164
|
+
"""Interpret a boolean environment variable value.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
value: The environment variable value.
|
|
168
|
+
field_name: The field name.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
The interpreted value.
|
|
172
|
+
|
|
173
|
+
Raises:
|
|
174
|
+
EnvironmentVarValueError: If the value is invalid.
|
|
175
|
+
"""
|
|
176
|
+
true_values = ["true", "1", "yes", "y"]
|
|
177
|
+
false_values = ["false", "0", "no", "n"]
|
|
178
|
+
|
|
179
|
+
if value.lower() in true_values:
|
|
180
|
+
return True
|
|
181
|
+
elif value.lower() in false_values:
|
|
182
|
+
return False
|
|
183
|
+
raise EnvironmentVarValueError(f"Invalid boolean value: {value} for {field_name}")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def interpret_int_env(value: str, field_name: str) -> int:
|
|
187
|
+
"""Interpret an integer environment variable value.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
value: The environment variable value.
|
|
191
|
+
field_name: The field name.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
The interpreted value.
|
|
195
|
+
|
|
196
|
+
Raises:
|
|
197
|
+
EnvironmentVarValueError: If the value is invalid.
|
|
198
|
+
"""
|
|
199
|
+
try:
|
|
200
|
+
return int(value)
|
|
201
|
+
except ValueError as ve:
|
|
202
|
+
raise EnvironmentVarValueError(
|
|
203
|
+
f"Invalid integer value: {value} for {field_name}"
|
|
204
|
+
) from ve
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def interpret_existing_path_env(value: str, field_name: str) -> ExistingPath:
|
|
208
|
+
"""Interpret a path environment variable value as an existing path.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
value: The environment variable value.
|
|
212
|
+
field_name: The field name.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
The interpreted value.
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
EnvironmentVarValueError: If the path does not exist.
|
|
219
|
+
"""
|
|
220
|
+
path = Path(value)
|
|
221
|
+
if not path.exists():
|
|
222
|
+
raise EnvironmentVarValueError(f"Path does not exist: {path} for {field_name}")
|
|
223
|
+
return path
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def interpret_path_env(value: str, field_name: str) -> Path:
|
|
227
|
+
"""Interpret a path environment variable value.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
value: The environment variable value.
|
|
231
|
+
field_name: The field name.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
The interpreted value.
|
|
235
|
+
"""
|
|
236
|
+
return Path(value)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def interpret_enum_env(value: str, field_type: GenericType, field_name: str) -> Any:
|
|
240
|
+
"""Interpret an enum environment variable value.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
value: The environment variable value.
|
|
244
|
+
field_type: The field type.
|
|
245
|
+
field_name: The field name.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
The interpreted value.
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
EnvironmentVarValueError: If the value is invalid.
|
|
252
|
+
"""
|
|
253
|
+
try:
|
|
254
|
+
return field_type(value)
|
|
255
|
+
except ValueError as ve:
|
|
256
|
+
raise EnvironmentVarValueError(
|
|
257
|
+
f"Invalid enum value: {value} for {field_name}"
|
|
258
|
+
) from ve
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def interpret_env_var_value(
|
|
262
|
+
value: str, field_type: GenericType, field_name: str
|
|
263
|
+
) -> Any:
|
|
264
|
+
"""Interpret an environment variable value based on the field type.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
value: The environment variable value.
|
|
268
|
+
field_type: The field type.
|
|
269
|
+
field_name: The field name.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
The interpreted value.
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
ValueError: If the value is invalid.
|
|
276
|
+
"""
|
|
277
|
+
field_type = value_inside_optional(field_type)
|
|
278
|
+
|
|
279
|
+
if is_union(field_type):
|
|
280
|
+
raise ValueError(
|
|
281
|
+
f"Union types are not supported for environment variables: {field_name}."
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
if field_type is bool:
|
|
285
|
+
return interpret_boolean_env(value, field_name)
|
|
286
|
+
elif field_type is str:
|
|
287
|
+
return value
|
|
288
|
+
elif field_type is int:
|
|
289
|
+
return interpret_int_env(value, field_name)
|
|
290
|
+
elif field_type is Path:
|
|
291
|
+
return interpret_path_env(value, field_name)
|
|
292
|
+
elif field_type is ExistingPath:
|
|
293
|
+
return interpret_existing_path_env(value, field_name)
|
|
294
|
+
elif inspect.isclass(field_type) and issubclass(field_type, enum.Enum):
|
|
295
|
+
return interpret_enum_env(value, field_type, field_name)
|
|
296
|
+
|
|
297
|
+
else:
|
|
298
|
+
raise ValueError(
|
|
299
|
+
f"Invalid type for environment variable {field_name}: {field_type}. This is probably an issue in Reflex."
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class PathExistsFlag:
|
|
304
|
+
"""Flag to indicate that a path must exist."""
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
ExistingPath = Annotated[Path, PathExistsFlag]
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@dataclasses.dataclass(init=False)
|
|
311
|
+
class EnvironmentVariables:
|
|
312
|
+
"""Environment variables class to instantiate environment variables."""
|
|
313
|
+
|
|
314
|
+
# Whether to use npm over bun to install frontend packages.
|
|
315
|
+
REFLEX_USE_NPM: bool = False
|
|
316
|
+
|
|
317
|
+
# The npm registry to use.
|
|
318
|
+
NPM_CONFIG_REGISTRY: Optional[str] = None
|
|
319
|
+
|
|
320
|
+
# Whether to use Granian for the backend. Otherwise, use Uvicorn.
|
|
321
|
+
REFLEX_USE_GRANIAN: bool = False
|
|
322
|
+
|
|
323
|
+
# The username to use for authentication on python package repository. Username and password must both be provided.
|
|
324
|
+
TWINE_USERNAME: Optional[str] = None
|
|
325
|
+
|
|
326
|
+
# The password to use for authentication on python package repository. Username and password must both be provided.
|
|
327
|
+
TWINE_PASSWORD: Optional[str] = None
|
|
328
|
+
|
|
329
|
+
# Whether to use the system installed bun. If set to false, bun will be bundled with the app.
|
|
330
|
+
REFLEX_USE_SYSTEM_BUN: bool = False
|
|
331
|
+
|
|
332
|
+
# Whether to use the system installed node and npm. If set to false, node and npm will be bundled with the app.
|
|
333
|
+
REFLEX_USE_SYSTEM_NODE: bool = False
|
|
334
|
+
|
|
335
|
+
# The working directory for the next.js commands.
|
|
336
|
+
REFLEX_WEB_WORKDIR: Path = Path(constants.Dirs.WEB)
|
|
337
|
+
|
|
338
|
+
# Path to the alembic config file
|
|
339
|
+
ALEMBIC_CONFIG: ExistingPath = Path(constants.ALEMBIC_CONFIG)
|
|
340
|
+
|
|
341
|
+
# Disable SSL verification for HTTPX requests.
|
|
342
|
+
SSL_NO_VERIFY: bool = False
|
|
343
|
+
|
|
344
|
+
# The directory to store uploaded files.
|
|
345
|
+
REFLEX_UPLOADED_FILES_DIR: Path = Path(constants.Dirs.UPLOADED_FILES)
|
|
346
|
+
|
|
347
|
+
# Whether to use seperate processes to compile the frontend and how many. If not set, defaults to thread executor.
|
|
348
|
+
REFLEX_COMPILE_PROCESSES: Optional[int] = None
|
|
349
|
+
|
|
350
|
+
# Whether to use seperate threads to compile the frontend and how many. Defaults to `min(32, os.cpu_count() + 4)`.
|
|
351
|
+
REFLEX_COMPILE_THREADS: Optional[int] = None
|
|
352
|
+
|
|
353
|
+
# The directory to store reflex dependencies.
|
|
354
|
+
REFLEX_DIR: Path = Path(constants.Reflex.DIR)
|
|
355
|
+
|
|
356
|
+
# Whether to print the SQL queries if the log level is INFO or lower.
|
|
357
|
+
SQLALCHEMY_ECHO: bool = False
|
|
358
|
+
|
|
359
|
+
# Whether to ignore the redis config error. Some redis servers only allow out-of-band configuration.
|
|
360
|
+
REFLEX_IGNORE_REDIS_CONFIG_ERROR: bool = False
|
|
361
|
+
|
|
362
|
+
# Whether to skip purging the web directory in dev mode.
|
|
363
|
+
REFLEX_PERSIST_WEB_DIR: bool = False
|
|
364
|
+
|
|
365
|
+
# The reflex.build frontend host.
|
|
366
|
+
REFLEX_BUILD_FRONTEND: str = constants.Templates.REFLEX_BUILD_FRONTEND
|
|
367
|
+
|
|
368
|
+
# The reflex.build backend host.
|
|
369
|
+
REFLEX_BUILD_BACKEND: str = constants.Templates.REFLEX_BUILD_BACKEND
|
|
370
|
+
|
|
371
|
+
def __init__(self):
|
|
372
|
+
"""Initialize the environment variables."""
|
|
373
|
+
type_hints = get_type_hints(type(self))
|
|
374
|
+
|
|
375
|
+
for field in dataclasses.fields(self):
|
|
376
|
+
raw_value = os.getenv(field.name, None)
|
|
377
|
+
|
|
378
|
+
field.type = type_hints.get(field.name) or field.type
|
|
379
|
+
|
|
380
|
+
value = (
|
|
381
|
+
interpret_env_var_value(raw_value, field.type, field.name)
|
|
382
|
+
if raw_value is not None
|
|
383
|
+
else get_default_value_for_field(field)
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
setattr(self, field.name, value)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
environment = EnvironmentVariables()
|
|
390
|
+
|
|
391
|
+
|
|
134
392
|
class Config(Base):
|
|
135
393
|
"""The config defines runtime settings for the app.
|
|
136
394
|
|
|
@@ -191,7 +449,7 @@ class Config(Base):
|
|
|
191
449
|
telemetry_enabled: bool = True
|
|
192
450
|
|
|
193
451
|
# The bun path
|
|
194
|
-
bun_path:
|
|
452
|
+
bun_path: ExistingPath = constants.Bun.DEFAULT_PATH
|
|
195
453
|
|
|
196
454
|
# List of origins that are allowed to connect to the backend API.
|
|
197
455
|
cors_allowed_origins: List[str] = ["*"]
|
|
@@ -222,6 +480,12 @@ class Config(Base):
|
|
|
222
480
|
# Number of gunicorn workers from user
|
|
223
481
|
gunicorn_workers: Optional[int] = None
|
|
224
482
|
|
|
483
|
+
# Number of requests before a worker is restarted
|
|
484
|
+
gunicorn_max_requests: int = 100
|
|
485
|
+
|
|
486
|
+
# Variance limit for max requests; gunicorn only
|
|
487
|
+
gunicorn_max_requests_jitter: int = 25
|
|
488
|
+
|
|
225
489
|
# Indicate which type of state manager to use
|
|
226
490
|
state_manager_mode: constants.StateManagerMode = constants.StateManagerMode.DISK
|
|
227
491
|
|
|
@@ -234,6 +498,9 @@ class Config(Base):
|
|
|
234
498
|
# Attributes that were explicitly set by the user.
|
|
235
499
|
_non_default_attributes: Set[str] = pydantic.PrivateAttr(set())
|
|
236
500
|
|
|
501
|
+
# Path to file containing key-values pairs to override in the environment; Dotenv format.
|
|
502
|
+
env_file: Optional[str] = None
|
|
503
|
+
|
|
237
504
|
def __init__(self, *args, **kwargs):
|
|
238
505
|
"""Initialize the config values.
|
|
239
506
|
|
|
@@ -275,14 +542,21 @@ class Config(Base):
|
|
|
275
542
|
|
|
276
543
|
def update_from_env(self) -> dict[str, Any]:
|
|
277
544
|
"""Update the config values based on set environment variables.
|
|
545
|
+
If there is a set env_file, it is loaded first.
|
|
278
546
|
|
|
279
547
|
Returns:
|
|
280
548
|
The updated config values.
|
|
281
|
-
|
|
282
|
-
Raises:
|
|
283
|
-
EnvVarValueError: If an environment variable is set to an invalid type.
|
|
284
549
|
"""
|
|
285
|
-
|
|
550
|
+
if self.env_file:
|
|
551
|
+
try:
|
|
552
|
+
from dotenv import load_dotenv # type: ignore
|
|
553
|
+
|
|
554
|
+
# load env file if exists
|
|
555
|
+
load_dotenv(self.env_file, override=True)
|
|
556
|
+
except ImportError:
|
|
557
|
+
console.error(
|
|
558
|
+
"""The `python-dotenv` package is required to load environment variables from a file. Run `pip install "python-dotenv>=1.0.1"`."""
|
|
559
|
+
)
|
|
286
560
|
|
|
287
561
|
updated_values = {}
|
|
288
562
|
# Iterate over the fields.
|
|
@@ -298,21 +572,11 @@ class Config(Base):
|
|
|
298
572
|
dedupe=True,
|
|
299
573
|
)
|
|
300
574
|
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
if issubclass(field.type_, bool):
|
|
304
|
-
# special handling for bool values
|
|
305
|
-
env_var = env_var.lower() in ["true", "1", "yes"]
|
|
306
|
-
else:
|
|
307
|
-
env_var = field.type_(env_var)
|
|
308
|
-
except ValueError as ve:
|
|
309
|
-
console.error(
|
|
310
|
-
f"Could not convert {key.upper()}={env_var} to type {field.type_}"
|
|
311
|
-
)
|
|
312
|
-
raise EnvVarValueError from ve
|
|
575
|
+
# Interpret the value.
|
|
576
|
+
value = interpret_env_var_value(env_var, field.outer_type_, field.name)
|
|
313
577
|
|
|
314
578
|
# Set the value.
|
|
315
|
-
updated_values[key] =
|
|
579
|
+
updated_values[key] = value
|
|
316
580
|
|
|
317
581
|
return updated_values
|
|
318
582
|
|
reflex/constants/__init__.py
CHANGED
reflex/constants/base.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import os
|
|
6
5
|
import platform
|
|
7
6
|
from enum import Enum
|
|
8
7
|
from importlib import metadata
|
|
@@ -11,6 +10,8 @@ from types import SimpleNamespace
|
|
|
11
10
|
|
|
12
11
|
from platformdirs import PlatformDirs
|
|
13
12
|
|
|
13
|
+
from .utils import classproperty
|
|
14
|
+
|
|
14
15
|
IS_WINDOWS = platform.system() == "Windows"
|
|
15
16
|
|
|
16
17
|
|
|
@@ -20,6 +21,8 @@ class Dirs(SimpleNamespace):
|
|
|
20
21
|
# The frontend directories in a project.
|
|
21
22
|
# The web folder where the NextJS app is compiled to.
|
|
22
23
|
WEB = ".web"
|
|
24
|
+
# The directory where uploaded files are stored.
|
|
25
|
+
UPLOADED_FILES = "uploaded_files"
|
|
23
26
|
# The name of the assets directory.
|
|
24
27
|
APP_ASSETS = "assets"
|
|
25
28
|
# The name of the assets directory for external ressource (a subfolder of APP_ASSETS).
|
|
@@ -64,21 +67,13 @@ class Reflex(SimpleNamespace):
|
|
|
64
67
|
|
|
65
68
|
# Files and directories used to init a new project.
|
|
66
69
|
# The directory to store reflex dependencies.
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
or (
|
|
73
|
-
# on windows, we use C:/Users/<username>/AppData/Local/reflex.
|
|
74
|
-
# on macOS, we use ~/Library/Application Support/reflex.
|
|
75
|
-
# on linux, we use ~/.local/share/reflex.
|
|
76
|
-
# If user sets REFLEX_DIR envroment variable use that instead.
|
|
77
|
-
PlatformDirs(MODULE_NAME, False).user_data_dir
|
|
78
|
-
)
|
|
79
|
-
)
|
|
80
|
-
# The root directory of the reflex library.
|
|
70
|
+
# on windows, we use C:/Users/<username>/AppData/Local/reflex.
|
|
71
|
+
# on macOS, we use ~/Library/Application Support/reflex.
|
|
72
|
+
# on linux, we use ~/.local/share/reflex.
|
|
73
|
+
# If user sets REFLEX_DIR envroment variable use that instead.
|
|
74
|
+
DIR = PlatformDirs(MODULE_NAME, False).user_data_path
|
|
81
75
|
|
|
76
|
+
# The root directory of the reflex library.
|
|
82
77
|
ROOT_DIR = Path(__file__).parents[2]
|
|
83
78
|
|
|
84
79
|
RELEASES_URL = f"https://api.github.com/repos/reflex-dev/templates/releases"
|
|
@@ -101,27 +96,51 @@ class Templates(SimpleNamespace):
|
|
|
101
96
|
DEFAULT = "blank"
|
|
102
97
|
|
|
103
98
|
# The reflex.build frontend host
|
|
104
|
-
REFLEX_BUILD_FRONTEND =
|
|
105
|
-
"REFLEX_BUILD_FRONTEND", "https://flexgen.reflex.run"
|
|
106
|
-
)
|
|
99
|
+
REFLEX_BUILD_FRONTEND = "https://flexgen.reflex.run"
|
|
107
100
|
|
|
108
101
|
# The reflex.build backend host
|
|
109
|
-
REFLEX_BUILD_BACKEND =
|
|
110
|
-
|
|
111
|
-
|
|
102
|
+
REFLEX_BUILD_BACKEND = "https://flexgen-prod-flexgen.fly.dev"
|
|
103
|
+
|
|
104
|
+
@classproperty
|
|
105
|
+
@classmethod
|
|
106
|
+
def REFLEX_BUILD_URL(cls):
|
|
107
|
+
"""The URL to redirect to reflex.build.
|
|
112
108
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
Returns:
|
|
110
|
+
The URL to redirect to reflex.build.
|
|
111
|
+
"""
|
|
112
|
+
from reflex.config import environment
|
|
117
113
|
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
return (
|
|
115
|
+
environment.REFLEX_BUILD_FRONTEND
|
|
116
|
+
+ "/gen?reflex_init_token={reflex_init_token}"
|
|
117
|
+
)
|
|
120
118
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
@classproperty
|
|
120
|
+
@classmethod
|
|
121
|
+
def REFLEX_BUILD_POLL_URL(cls):
|
|
122
|
+
"""The URL to poll waiting for the user to select a generation.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
The URL to poll waiting for the user to select a generation.
|
|
126
|
+
"""
|
|
127
|
+
from reflex.config import environment
|
|
128
|
+
|
|
129
|
+
return environment.REFLEX_BUILD_BACKEND + "/api/init/{reflex_init_token}"
|
|
130
|
+
|
|
131
|
+
@classproperty
|
|
132
|
+
@classmethod
|
|
133
|
+
def REFLEX_BUILD_CODE_URL(cls):
|
|
134
|
+
"""The URL to fetch the generation's reflex code.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
The URL to fetch the generation's reflex code.
|
|
138
|
+
"""
|
|
139
|
+
from reflex.config import environment
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
environment.REFLEX_BUILD_BACKEND + "/api/gen/{generation_hash}/refactored"
|
|
143
|
+
)
|
|
125
144
|
|
|
126
145
|
class Dirs(SimpleNamespace):
|
|
127
146
|
"""Folders used by the template system of Reflex."""
|
|
@@ -226,6 +245,9 @@ SKIP_COMPILE_ENV_VAR = "__REFLEX_SKIP_COMPILE"
|
|
|
226
245
|
# This env var stores the execution mode of the app
|
|
227
246
|
ENV_MODE_ENV_VAR = "REFLEX_ENV_MODE"
|
|
228
247
|
|
|
248
|
+
ENV_BACKEND_ONLY_ENV_VAR = "REFLEX_BACKEND_ONLY"
|
|
249
|
+
ENV_FRONTEND_ONLY_ENV_VAR = "REFLEX_FRONTEND_ONLY"
|
|
250
|
+
|
|
229
251
|
# Testing variables.
|
|
230
252
|
# Testing os env set by pytest when running a test case.
|
|
231
253
|
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
|
reflex/constants/compiler.py
CHANGED
|
@@ -114,8 +114,8 @@ class Imports(SimpleNamespace):
|
|
|
114
114
|
|
|
115
115
|
EVENTS = {
|
|
116
116
|
"react": [ImportVar(tag="useContext")],
|
|
117
|
-
f"
|
|
118
|
-
f"
|
|
117
|
+
f"$/{Dirs.CONTEXTS_PATH}": [ImportVar(tag="EventLoopContext")],
|
|
118
|
+
f"$/{Dirs.STATE_PATH}": [ImportVar(tag=CompileVars.TO_EVENT)],
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
|
|
@@ -132,16 +132,6 @@ class Hooks(SimpleNamespace):
|
|
|
132
132
|
}
|
|
133
133
|
})"""
|
|
134
134
|
|
|
135
|
-
FRONTEND_ERRORS = f"""
|
|
136
|
-
const logFrontendError = (error, info) => {{
|
|
137
|
-
if (process.env.NODE_ENV === "production") {{
|
|
138
|
-
addEvents([Event("{CompileVars.FRONTEND_EXCEPTION_STATE_FULL}.handle_frontend_exception", {{
|
|
139
|
-
stack: error.stack,
|
|
140
|
-
}})])
|
|
141
|
-
}}
|
|
142
|
-
}}
|
|
143
|
-
"""
|
|
144
|
-
|
|
145
135
|
|
|
146
136
|
class MemoizationDisposition(enum.Enum):
|
|
147
137
|
"""The conditions under which a component should be memoized."""
|