reflex 0.6.6.post3__py3-none-any.whl → 0.6.7a1__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/stateful_component.js.jinja2 +5 -1
- reflex/.templates/web/utils/state.js +36 -28
- reflex/__init__.py +1 -1
- reflex/__init__.pyi +1 -0
- reflex/app.py +41 -16
- reflex/assets.py +2 -2
- reflex/base.py +8 -7
- reflex/compiler/templates.py +1 -0
- reflex/compiler/utils.py +2 -3
- reflex/components/base/bare.py +2 -2
- reflex/components/component.py +54 -29
- reflex/components/core/banner.py +2 -2
- reflex/components/core/banner.pyi +1 -1
- reflex/components/core/client_side_routing.py +2 -2
- reflex/components/core/client_side_routing.pyi +1 -1
- reflex/components/core/clipboard.py +11 -9
- reflex/components/core/clipboard.pyi +1 -1
- reflex/components/core/cond.py +3 -3
- reflex/components/core/foreach.py +1 -1
- reflex/components/core/html.pyi +1 -1
- reflex/components/core/upload.py +8 -8
- reflex/components/datadisplay/code.py +5 -5
- reflex/components/datadisplay/dataeditor.py +8 -28
- reflex/components/datadisplay/dataeditor.pyi +1 -1
- reflex/components/datadisplay/shiki_code_block.py +7 -7
- reflex/components/dynamic.py +2 -2
- reflex/components/el/elements/__init__.py +1 -1
- reflex/components/el/elements/__init__.pyi +1 -1
- reflex/components/el/elements/base.py +2 -2
- reflex/components/el/elements/base.pyi +1 -1
- reflex/components/el/elements/forms.py +40 -10
- reflex/components/el/elements/forms.pyi +17 -15
- reflex/components/el/elements/inline.py +1 -1
- reflex/components/el/elements/inline.pyi +28 -28
- reflex/components/el/elements/media.py +1 -4
- reflex/components/el/elements/media.pyi +25 -26
- reflex/components/el/elements/metadata.py +6 -6
- reflex/components/el/elements/metadata.pyi +4 -4
- reflex/components/el/elements/other.py +17 -9
- reflex/components/el/elements/other.pyi +7 -7
- reflex/components/el/elements/scripts.py +1 -2
- reflex/components/el/elements/scripts.pyi +3 -3
- reflex/components/el/elements/sectioning.py +16 -16
- reflex/components/el/elements/sectioning.pyi +15 -15
- reflex/components/el/elements/tables.py +1 -1
- reflex/components/el/elements/tables.pyi +10 -10
- reflex/components/el/elements/typography.py +1 -1
- reflex/components/el/elements/typography.pyi +15 -15
- reflex/components/markdown/markdown.py +3 -3
- reflex/components/next/image.py +1 -1
- reflex/components/next/image.pyi +1 -1
- reflex/components/plotly/plotly.py +2 -2
- reflex/components/radix/primitives/accordion.py +2 -1
- reflex/components/radix/primitives/form.pyi +3 -3
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/themes/base.py +4 -10
- reflex/components/radix/themes/color_mode.pyi +2 -2
- reflex/components/radix/themes/components/alert_dialog.pyi +1 -1
- reflex/components/radix/themes/components/badge.pyi +1 -1
- reflex/components/radix/themes/components/button.pyi +1 -1
- reflex/components/radix/themes/components/callout.pyi +5 -5
- reflex/components/radix/themes/components/card.pyi +1 -1
- reflex/components/radix/themes/components/checkbox.pyi +3 -3
- reflex/components/radix/themes/components/context_menu.py +11 -0
- reflex/components/radix/themes/components/context_menu.pyi +155 -0
- reflex/components/radix/themes/components/dialog.pyi +1 -1
- reflex/components/radix/themes/components/hover_card.pyi +1 -1
- reflex/components/radix/themes/components/icon_button.py +1 -1
- reflex/components/radix/themes/components/icon_button.pyi +1 -1
- reflex/components/radix/themes/components/inset.pyi +1 -1
- reflex/components/radix/themes/components/popover.pyi +1 -1
- reflex/components/radix/themes/components/radio_group.py +2 -4
- reflex/components/radix/themes/components/radio_group.pyi +1 -1
- reflex/components/radix/themes/components/select.pyi +3 -3
- reflex/components/radix/themes/components/slider.pyi +1 -1
- reflex/components/radix/themes/components/switch.pyi +1 -1
- reflex/components/radix/themes/components/table.pyi +7 -7
- reflex/components/radix/themes/components/tabs.pyi +2 -2
- reflex/components/radix/themes/components/text_area.py +3 -0
- reflex/components/radix/themes/components/text_area.pyi +3 -1
- reflex/components/radix/themes/components/text_field.py +16 -1
- reflex/components/radix/themes/components/text_field.pyi +105 -17
- reflex/components/radix/themes/layout/box.pyi +1 -1
- reflex/components/radix/themes/layout/center.pyi +1 -1
- reflex/components/radix/themes/layout/flex.pyi +1 -1
- reflex/components/radix/themes/layout/grid.pyi +1 -1
- reflex/components/radix/themes/layout/list.py +0 -4
- reflex/components/radix/themes/layout/list.pyi +3 -8
- reflex/components/radix/themes/layout/section.pyi +1 -1
- reflex/components/radix/themes/layout/spacer.pyi +1 -1
- reflex/components/radix/themes/layout/stack.pyi +3 -3
- reflex/components/radix/themes/typography/blockquote.pyi +1 -1
- reflex/components/radix/themes/typography/code.pyi +1 -1
- reflex/components/radix/themes/typography/heading.pyi +1 -1
- reflex/components/radix/themes/typography/link.py +5 -1
- reflex/components/radix/themes/typography/link.pyi +1 -1
- reflex/components/radix/themes/typography/text.pyi +7 -7
- reflex/components/recharts/cartesian.py +1 -1
- reflex/components/recharts/charts.py +4 -4
- reflex/components/recharts/polar.py +1 -1
- reflex/components/recharts/polar.pyi +1 -1
- reflex/components/sonner/toast.py +4 -7
- reflex/components/suneditor/editor.py +6 -6
- reflex/components/suneditor/editor.pyi +6 -6
- reflex/config.py +25 -10
- reflex/constants/compiler.py +6 -0
- reflex/constants/config.py +2 -0
- reflex/constants/custom_components.py +1 -1
- reflex/constants/route.py +1 -1
- reflex/custom_components/custom_components.py +21 -21
- reflex/event.py +57 -22
- reflex/experimental/client_state.py +2 -1
- reflex/experimental/layout.py +0 -6
- reflex/model.py +125 -9
- reflex/reflex.py +5 -6
- reflex/state.py +203 -88
- reflex/style.py +1 -4
- reflex/testing.py +10 -11
- reflex/utils/build.py +1 -1
- reflex/utils/console.py +75 -6
- reflex/utils/exceptions.py +12 -0
- reflex/utils/exec.py +10 -10
- reflex/utils/export.py +1 -2
- reflex/utils/format.py +11 -8
- reflex/utils/path_ops.py +2 -2
- reflex/utils/prerequisites.py +31 -28
- reflex/utils/processes.py +4 -4
- reflex/utils/pyi_generator.py +12 -11
- reflex/utils/types.py +6 -3
- reflex/vars/__init__.py +1 -0
- reflex/vars/base.py +75 -38
- reflex/vars/datetime.py +222 -0
- reflex/vars/function.py +3 -3
- reflex/vars/number.py +3 -3
- reflex/vars/object.py +5 -5
- reflex/vars/sequence.py +7 -7
- {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/METADATA +2 -2
- {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/RECORD +141 -140
- {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/LICENSE +0 -0
- {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/WHEEL +0 -0
- {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/entry_points.txt +0 -0
reflex/utils/console.py
CHANGED
|
@@ -20,13 +20,46 @@ _EMITTED_DEPRECATION_WARNINGS = set()
|
|
|
20
20
|
# Info messages which have been printed.
|
|
21
21
|
_EMITTED_INFO = set()
|
|
22
22
|
|
|
23
|
+
# Warnings which have been printed.
|
|
24
|
+
_EMIITED_WARNINGS = set()
|
|
25
|
+
|
|
26
|
+
# Errors which have been printed.
|
|
27
|
+
_EMITTED_ERRORS = set()
|
|
28
|
+
|
|
29
|
+
# Success messages which have been printed.
|
|
30
|
+
_EMITTED_SUCCESS = set()
|
|
31
|
+
|
|
32
|
+
# Debug messages which have been printed.
|
|
33
|
+
_EMITTED_DEBUG = set()
|
|
34
|
+
|
|
35
|
+
# Logs which have been printed.
|
|
36
|
+
_EMITTED_LOGS = set()
|
|
37
|
+
|
|
38
|
+
# Prints which have been printed.
|
|
39
|
+
_EMITTED_PRINTS = set()
|
|
40
|
+
|
|
23
41
|
|
|
24
42
|
def set_log_level(log_level: LogLevel):
|
|
25
43
|
"""Set the log level.
|
|
26
44
|
|
|
27
45
|
Args:
|
|
28
46
|
log_level: The log level to set.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ValueError: If the log level is invalid.
|
|
29
50
|
"""
|
|
51
|
+
if not isinstance(log_level, LogLevel):
|
|
52
|
+
deprecate(
|
|
53
|
+
feature_name="Passing a string to set_log_level",
|
|
54
|
+
reason="use reflex.constants.LogLevel enum instead",
|
|
55
|
+
deprecation_version="0.6.6",
|
|
56
|
+
removal_version="0.7.0",
|
|
57
|
+
)
|
|
58
|
+
try:
|
|
59
|
+
log_level = getattr(LogLevel, log_level.upper())
|
|
60
|
+
except AttributeError as ae:
|
|
61
|
+
raise ValueError(f"Invalid log level: {log_level}") from ae
|
|
62
|
+
|
|
30
63
|
global _LOG_LEVEL
|
|
31
64
|
_LOG_LEVEL = log_level
|
|
32
65
|
|
|
@@ -40,25 +73,37 @@ def is_debug() -> bool:
|
|
|
40
73
|
return _LOG_LEVEL <= LogLevel.DEBUG
|
|
41
74
|
|
|
42
75
|
|
|
43
|
-
def print(msg: str, **kwargs):
|
|
76
|
+
def print(msg: str, dedupe: bool = False, **kwargs):
|
|
44
77
|
"""Print a message.
|
|
45
78
|
|
|
46
79
|
Args:
|
|
47
80
|
msg: The message to print.
|
|
81
|
+
dedupe: If True, suppress multiple console logs of print message.
|
|
48
82
|
kwargs: Keyword arguments to pass to the print function.
|
|
49
83
|
"""
|
|
84
|
+
if dedupe:
|
|
85
|
+
if msg in _EMITTED_PRINTS:
|
|
86
|
+
return
|
|
87
|
+
else:
|
|
88
|
+
_EMITTED_PRINTS.add(msg)
|
|
50
89
|
_console.print(msg, **kwargs)
|
|
51
90
|
|
|
52
91
|
|
|
53
|
-
def debug(msg: str, **kwargs):
|
|
92
|
+
def debug(msg: str, dedupe: bool = False, **kwargs):
|
|
54
93
|
"""Print a debug message.
|
|
55
94
|
|
|
56
95
|
Args:
|
|
57
96
|
msg: The debug message.
|
|
97
|
+
dedupe: If True, suppress multiple console logs of debug message.
|
|
58
98
|
kwargs: Keyword arguments to pass to the print function.
|
|
59
99
|
"""
|
|
60
100
|
if is_debug():
|
|
61
101
|
msg_ = f"[purple]Debug: {msg}[/purple]"
|
|
102
|
+
if dedupe:
|
|
103
|
+
if msg_ in _EMITTED_DEBUG:
|
|
104
|
+
return
|
|
105
|
+
else:
|
|
106
|
+
_EMITTED_DEBUG.add(msg_)
|
|
62
107
|
if progress := kwargs.pop("progress", None):
|
|
63
108
|
progress.console.print(msg_, **kwargs)
|
|
64
109
|
else:
|
|
@@ -82,25 +127,37 @@ def info(msg: str, dedupe: bool = False, **kwargs):
|
|
|
82
127
|
print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
|
|
83
128
|
|
|
84
129
|
|
|
85
|
-
def success(msg: str, **kwargs):
|
|
130
|
+
def success(msg: str, dedupe: bool = False, **kwargs):
|
|
86
131
|
"""Print a success message.
|
|
87
132
|
|
|
88
133
|
Args:
|
|
89
134
|
msg: The success message.
|
|
135
|
+
dedupe: If True, suppress multiple console logs of success message.
|
|
90
136
|
kwargs: Keyword arguments to pass to the print function.
|
|
91
137
|
"""
|
|
92
138
|
if _LOG_LEVEL <= LogLevel.INFO:
|
|
139
|
+
if dedupe:
|
|
140
|
+
if msg in _EMITTED_SUCCESS:
|
|
141
|
+
return
|
|
142
|
+
else:
|
|
143
|
+
_EMITTED_SUCCESS.add(msg)
|
|
93
144
|
print(f"[green]Success: {msg}[/green]", **kwargs)
|
|
94
145
|
|
|
95
146
|
|
|
96
|
-
def log(msg: str, **kwargs):
|
|
147
|
+
def log(msg: str, dedupe: bool = False, **kwargs):
|
|
97
148
|
"""Takes a string and logs it to the console.
|
|
98
149
|
|
|
99
150
|
Args:
|
|
100
151
|
msg: The message to log.
|
|
152
|
+
dedupe: If True, suppress multiple console logs of log message.
|
|
101
153
|
kwargs: Keyword arguments to pass to the print function.
|
|
102
154
|
"""
|
|
103
155
|
if _LOG_LEVEL <= LogLevel.INFO:
|
|
156
|
+
if dedupe:
|
|
157
|
+
if msg in _EMITTED_LOGS:
|
|
158
|
+
return
|
|
159
|
+
else:
|
|
160
|
+
_EMITTED_LOGS.add(msg)
|
|
104
161
|
_console.log(msg, **kwargs)
|
|
105
162
|
|
|
106
163
|
|
|
@@ -114,14 +171,20 @@ def rule(title: str, **kwargs):
|
|
|
114
171
|
_console.rule(title, **kwargs)
|
|
115
172
|
|
|
116
173
|
|
|
117
|
-
def warn(msg: str, **kwargs):
|
|
174
|
+
def warn(msg: str, dedupe: bool = False, **kwargs):
|
|
118
175
|
"""Print a warning message.
|
|
119
176
|
|
|
120
177
|
Args:
|
|
121
178
|
msg: The warning message.
|
|
179
|
+
dedupe: If True, suppress multiple console logs of warning message.
|
|
122
180
|
kwargs: Keyword arguments to pass to the print function.
|
|
123
181
|
"""
|
|
124
182
|
if _LOG_LEVEL <= LogLevel.WARNING:
|
|
183
|
+
if dedupe:
|
|
184
|
+
if msg in _EMIITED_WARNINGS:
|
|
185
|
+
return
|
|
186
|
+
else:
|
|
187
|
+
_EMIITED_WARNINGS.add(msg)
|
|
125
188
|
print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
|
|
126
189
|
|
|
127
190
|
|
|
@@ -154,14 +217,20 @@ def deprecate(
|
|
|
154
217
|
_EMITTED_DEPRECATION_WARNINGS.add(feature_name)
|
|
155
218
|
|
|
156
219
|
|
|
157
|
-
def error(msg: str, **kwargs):
|
|
220
|
+
def error(msg: str, dedupe: bool = False, **kwargs):
|
|
158
221
|
"""Print an error message.
|
|
159
222
|
|
|
160
223
|
Args:
|
|
161
224
|
msg: The error message.
|
|
225
|
+
dedupe: If True, suppress multiple console logs of error message.
|
|
162
226
|
kwargs: Keyword arguments to pass to the print function.
|
|
163
227
|
"""
|
|
164
228
|
if _LOG_LEVEL <= LogLevel.ERROR:
|
|
229
|
+
if dedupe:
|
|
230
|
+
if msg in _EMITTED_ERRORS:
|
|
231
|
+
return
|
|
232
|
+
else:
|
|
233
|
+
_EMITTED_ERRORS.add(msg)
|
|
165
234
|
print(f"[red]{msg}[/red]", **kwargs)
|
|
166
235
|
|
|
167
236
|
|
reflex/utils/exceptions.py
CHANGED
|
@@ -63,6 +63,10 @@ class UploadValueError(ReflexError, ValueError):
|
|
|
63
63
|
"""Custom ValueError for upload related errors."""
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
class PageValueError(ReflexError, ValueError):
|
|
67
|
+
"""Custom ValueError for page related errors."""
|
|
68
|
+
|
|
69
|
+
|
|
66
70
|
class RouteValueError(ReflexError, ValueError):
|
|
67
71
|
"""Custom ValueError for route related errors."""
|
|
68
72
|
|
|
@@ -155,6 +159,10 @@ class StateTooLargeError(ReflexError):
|
|
|
155
159
|
"""Raised when the state is too large to be serialized."""
|
|
156
160
|
|
|
157
161
|
|
|
162
|
+
class StateSerializationError(ReflexError):
|
|
163
|
+
"""Raised when the state cannot be serialized."""
|
|
164
|
+
|
|
165
|
+
|
|
158
166
|
class SystemPackageMissingError(ReflexError):
|
|
159
167
|
"""Raised when a system package is missing."""
|
|
160
168
|
|
|
@@ -175,3 +183,7 @@ def raise_system_package_missing_error(package: str) -> NoReturn:
|
|
|
175
183
|
" Please install it through your system package manager."
|
|
176
184
|
+ (f" You can do so by running 'brew install {package}'." if IS_MACOS else "")
|
|
177
185
|
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class InvalidLockWarningThresholdError(ReflexError):
|
|
189
|
+
"""Raised when an invalid lock warning threshold is provided."""
|
reflex/utils/exec.py
CHANGED
|
@@ -24,7 +24,7 @@ from reflex.utils.prerequisites import get_web_dir
|
|
|
24
24
|
frontend_process = None
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def detect_package_change(json_file_path:
|
|
27
|
+
def detect_package_change(json_file_path: Path) -> str:
|
|
28
28
|
"""Calculates the SHA-256 hash of a JSON file and returns it as a hexadecimal string.
|
|
29
29
|
|
|
30
30
|
Args:
|
|
@@ -37,7 +37,7 @@ def detect_package_change(json_file_path: str) -> str:
|
|
|
37
37
|
>>> detect_package_change("package.json")
|
|
38
38
|
'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2'
|
|
39
39
|
"""
|
|
40
|
-
with open(
|
|
40
|
+
with json_file_path.open("r") as file:
|
|
41
41
|
json_data = json.load(file)
|
|
42
42
|
|
|
43
43
|
# Calculate the hash
|
|
@@ -81,7 +81,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
|
|
|
81
81
|
from reflex.utils import processes
|
|
82
82
|
|
|
83
83
|
json_file_path = get_web_dir() / constants.PackageJson.PATH
|
|
84
|
-
last_hash = detect_package_change(
|
|
84
|
+
last_hash = detect_package_change(json_file_path)
|
|
85
85
|
process = None
|
|
86
86
|
first_run = True
|
|
87
87
|
|
|
@@ -117,14 +117,14 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
|
|
|
117
117
|
console.print("New packages detected: Updating app...")
|
|
118
118
|
else:
|
|
119
119
|
if any(
|
|
120
|
-
|
|
120
|
+
x in line for x in ("bin executable does not exist on disk",)
|
|
121
121
|
):
|
|
122
122
|
console.error(
|
|
123
123
|
"Try setting `REFLEX_USE_NPM=1` and re-running `reflex init` and `reflex run` to use npm instead of bun:\n"
|
|
124
124
|
"`REFLEX_USE_NPM=1 reflex init`\n"
|
|
125
125
|
"`REFLEX_USE_NPM=1 reflex run`"
|
|
126
126
|
)
|
|
127
|
-
new_hash = detect_package_change(
|
|
127
|
+
new_hash = detect_package_change(json_file_path)
|
|
128
128
|
if new_hash != last_hash:
|
|
129
129
|
last_hash = new_hash
|
|
130
130
|
kill(process.pid)
|
|
@@ -206,7 +206,9 @@ def get_granian_target():
|
|
|
206
206
|
|
|
207
207
|
app_module_path = Path(reflex.__file__).parent / "app_module_for_backend.py"
|
|
208
208
|
|
|
209
|
-
return
|
|
209
|
+
return (
|
|
210
|
+
f"{app_module_path!s}:{constants.CompileVars.APP}.{constants.CompileVars.API}"
|
|
211
|
+
)
|
|
210
212
|
|
|
211
213
|
|
|
212
214
|
def run_backend(
|
|
@@ -440,10 +442,8 @@ def output_system_info():
|
|
|
440
442
|
|
|
441
443
|
system = platform.system()
|
|
442
444
|
|
|
443
|
-
if (
|
|
444
|
-
system
|
|
445
|
-
or system == "Windows"
|
|
446
|
-
and prerequisites.is_windows_bun_supported()
|
|
445
|
+
if system != "Windows" or (
|
|
446
|
+
system == "Windows" and prerequisites.is_windows_bun_supported()
|
|
447
447
|
):
|
|
448
448
|
dependencies.extend(
|
|
449
449
|
[
|
reflex/utils/export.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""Export utilities."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
from typing import Optional
|
|
6
5
|
|
|
@@ -15,7 +14,7 @@ def export(
|
|
|
15
14
|
zipping: bool = True,
|
|
16
15
|
frontend: bool = True,
|
|
17
16
|
backend: bool = True,
|
|
18
|
-
zip_dest_dir: str =
|
|
17
|
+
zip_dest_dir: str = str(Path.cwd()),
|
|
19
18
|
upload_db_file: bool = False,
|
|
20
19
|
api_url: Optional[str] = None,
|
|
21
20
|
deploy_url: Optional[str] = None,
|
reflex/utils/format.py
CHANGED
|
@@ -329,12 +329,12 @@ def format_match(
|
|
|
329
329
|
return_value = case[-1]
|
|
330
330
|
|
|
331
331
|
case_conditions = " ".join(
|
|
332
|
-
[f"case JSON.stringify({
|
|
332
|
+
[f"case JSON.stringify({condition!s}):" for condition in conditions]
|
|
333
333
|
)
|
|
334
|
-
case_code = f"{case_conditions} return ({
|
|
334
|
+
case_code = f"{case_conditions} return ({return_value!s}); break;"
|
|
335
335
|
switch_code += case_code
|
|
336
336
|
|
|
337
|
-
switch_code += f"default: return ({
|
|
337
|
+
switch_code += f"default: return ({default!s}); break;"
|
|
338
338
|
switch_code += "};})()"
|
|
339
339
|
|
|
340
340
|
return switch_code
|
|
@@ -413,7 +413,7 @@ def format_props(*single_props, **key_value_props) -> list[str]:
|
|
|
413
413
|
)
|
|
414
414
|
for name, prop in sorted(key_value_props.items())
|
|
415
415
|
if prop is not None
|
|
416
|
-
] + [(f"{
|
|
416
|
+
] + [(f"{LiteralVar.create(prop)!s}") for prop in single_props]
|
|
417
417
|
|
|
418
418
|
|
|
419
419
|
def get_event_handler_parts(handler: EventHandler) -> tuple[str, str]:
|
|
@@ -664,18 +664,22 @@ def format_library_name(library_fullname: str):
|
|
|
664
664
|
return lib
|
|
665
665
|
|
|
666
666
|
|
|
667
|
-
def json_dumps(obj: Any) -> str:
|
|
667
|
+
def json_dumps(obj: Any, **kwargs) -> str:
|
|
668
668
|
"""Takes an object and returns a jsonified string.
|
|
669
669
|
|
|
670
670
|
Args:
|
|
671
671
|
obj: The object to be serialized.
|
|
672
|
+
kwargs: Additional keyword arguments to pass to json.dumps.
|
|
672
673
|
|
|
673
674
|
Returns:
|
|
674
675
|
A string
|
|
675
676
|
"""
|
|
676
677
|
from reflex.utils import serializers
|
|
677
678
|
|
|
678
|
-
|
|
679
|
+
kwargs.setdefault("ensure_ascii", False)
|
|
680
|
+
kwargs.setdefault("default", serializers.serialize)
|
|
681
|
+
|
|
682
|
+
return json.dumps(obj, **kwargs)
|
|
679
683
|
|
|
680
684
|
|
|
681
685
|
def collect_form_dict_names(form_dict: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -712,8 +716,7 @@ def format_array_ref(refs: str, idx: Var | None) -> str:
|
|
|
712
716
|
"""
|
|
713
717
|
clean_ref = re.sub(r"[^\w]+", "_", refs)
|
|
714
718
|
if idx is not None:
|
|
715
|
-
|
|
716
|
-
return f"refs_{clean_ref}[{str(idx)}]"
|
|
719
|
+
return f"refs_{clean_ref}[{idx!s}]"
|
|
717
720
|
return f"refs_{clean_ref}"
|
|
718
721
|
|
|
719
722
|
|
reflex/utils/path_ops.py
CHANGED
|
@@ -205,14 +205,14 @@ def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
|
|
|
205
205
|
# Read the existing json object from the file.
|
|
206
206
|
json_object = {}
|
|
207
207
|
if fp.stat().st_size:
|
|
208
|
-
with open(
|
|
208
|
+
with fp.open() as f:
|
|
209
209
|
json_object = json.load(f)
|
|
210
210
|
|
|
211
211
|
# Update the json object with the new data.
|
|
212
212
|
json_object.update(update_dict)
|
|
213
213
|
|
|
214
214
|
# Write the updated json object to the file
|
|
215
|
-
with open(
|
|
215
|
+
with fp.open("w") as f:
|
|
216
216
|
json.dump(json_object, f, ensure_ascii=False)
|
|
217
217
|
|
|
218
218
|
|
reflex/utils/prerequisites.py
CHANGED
|
@@ -219,9 +219,8 @@ def get_install_package_manager(on_failure_return_none: bool = False) -> str | N
|
|
|
219
219
|
Returns:
|
|
220
220
|
The path to the package manager.
|
|
221
221
|
"""
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
and not is_windows_bun_supported()
|
|
222
|
+
if constants.IS_WINDOWS and (
|
|
223
|
+
not is_windows_bun_supported()
|
|
225
224
|
or windows_check_onedrive_in_path()
|
|
226
225
|
or windows_npm_escape_hatch()
|
|
227
226
|
):
|
|
@@ -291,7 +290,7 @@ def get_app(reload: bool = False) -> ModuleType:
|
|
|
291
290
|
"If this error occurs in a reflex test case, ensure that `get_app` is mocked."
|
|
292
291
|
)
|
|
293
292
|
module = config.module
|
|
294
|
-
sys.path.insert(0,
|
|
293
|
+
sys.path.insert(0, str(Path.cwd()))
|
|
295
294
|
app = __import__(module, fromlist=(constants.CompileVars.APP,))
|
|
296
295
|
|
|
297
296
|
if reload:
|
|
@@ -439,9 +438,11 @@ def create_config(app_name: str):
|
|
|
439
438
|
from reflex.compiler import templates
|
|
440
439
|
|
|
441
440
|
config_name = f"{re.sub(r'[^a-zA-Z]', '', app_name).capitalize()}Config"
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
441
|
+
|
|
442
|
+
console.debug(f"Creating {constants.Config.FILE}")
|
|
443
|
+
constants.Config.FILE.write_text(
|
|
444
|
+
templates.RXCONFIG.render(app_name=app_name, config_name=config_name)
|
|
445
|
+
)
|
|
445
446
|
|
|
446
447
|
|
|
447
448
|
def initialize_gitignore(
|
|
@@ -495,14 +496,14 @@ def initialize_requirements_txt():
|
|
|
495
496
|
console.debug(f"Detected encoding for {fp} as {encoding}.")
|
|
496
497
|
try:
|
|
497
498
|
other_requirements_exist = False
|
|
498
|
-
with open(
|
|
499
|
-
for req in f
|
|
499
|
+
with fp.open("r", encoding=encoding) as f:
|
|
500
|
+
for req in f:
|
|
500
501
|
# Check if we have a package name that is reflex
|
|
501
502
|
if re.match(r"^reflex[^a-zA-Z0-9]", req):
|
|
502
503
|
console.debug(f"{fp} already has reflex as dependency.")
|
|
503
504
|
return
|
|
504
505
|
other_requirements_exist = True
|
|
505
|
-
with open(
|
|
506
|
+
with fp.open("a", encoding=encoding) as f:
|
|
506
507
|
preceding_newline = "\n" if other_requirements_exist else ""
|
|
507
508
|
f.write(
|
|
508
509
|
f"{preceding_newline}{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
|
|
@@ -700,7 +701,7 @@ def _update_next_config(
|
|
|
700
701
|
}
|
|
701
702
|
if transpile_packages:
|
|
702
703
|
next_config["transpilePackages"] = list(
|
|
703
|
-
|
|
704
|
+
{format_library_name(p) for p in transpile_packages}
|
|
704
705
|
)
|
|
705
706
|
if export:
|
|
706
707
|
next_config["output"] = "export"
|
|
@@ -733,13 +734,13 @@ def download_and_run(url: str, *args, show_status: bool = False, **env):
|
|
|
733
734
|
response.raise_for_status()
|
|
734
735
|
|
|
735
736
|
# Save the script to a temporary file.
|
|
736
|
-
script = tempfile.NamedTemporaryFile()
|
|
737
|
-
|
|
738
|
-
|
|
737
|
+
script = Path(tempfile.NamedTemporaryFile().name)
|
|
738
|
+
|
|
739
|
+
script.write_text(response.text)
|
|
739
740
|
|
|
740
741
|
# Run the script.
|
|
741
742
|
env = {**os.environ, **env}
|
|
742
|
-
process = processes.new_process(["bash",
|
|
743
|
+
process = processes.new_process(["bash", str(script), *args], env=env)
|
|
743
744
|
show = processes.show_status if show_status else processes.show_logs
|
|
744
745
|
show(f"Installing {url}", process)
|
|
745
746
|
|
|
@@ -753,14 +754,14 @@ def download_and_extract_fnm_zip():
|
|
|
753
754
|
# Download the zip file
|
|
754
755
|
url = constants.Fnm.INSTALL_URL
|
|
755
756
|
console.debug(f"Downloading {url}")
|
|
756
|
-
fnm_zip_file = constants.Fnm.DIR / f"{constants.Fnm.FILENAME}.zip"
|
|
757
|
+
fnm_zip_file: Path = constants.Fnm.DIR / f"{constants.Fnm.FILENAME}.zip"
|
|
757
758
|
# Function to download and extract the FNM zip release.
|
|
758
759
|
try:
|
|
759
760
|
# Download the FNM zip release.
|
|
760
761
|
# TODO: show progress to improve UX
|
|
761
762
|
response = net.get(url, follow_redirects=True)
|
|
762
763
|
response.raise_for_status()
|
|
763
|
-
with open(
|
|
764
|
+
with fnm_zip_file.open("wb") as output_file:
|
|
764
765
|
for chunk in response.iter_bytes():
|
|
765
766
|
output_file.write(chunk)
|
|
766
767
|
|
|
@@ -808,7 +809,7 @@ def install_node():
|
|
|
808
809
|
)
|
|
809
810
|
else: # All other platforms (Linux, MacOS).
|
|
810
811
|
# Add execute permissions to fnm executable.
|
|
811
|
-
|
|
812
|
+
constants.Fnm.EXE.chmod(stat.S_IXUSR)
|
|
812
813
|
# Install node.
|
|
813
814
|
# Specify arm64 arch explicitly for M1s and M2s.
|
|
814
815
|
architecture_arg = (
|
|
@@ -834,7 +835,7 @@ def install_bun():
|
|
|
834
835
|
"""Install bun onto the user's system."""
|
|
835
836
|
win_supported = is_windows_bun_supported()
|
|
836
837
|
one_drive_in_path = windows_check_onedrive_in_path()
|
|
837
|
-
if constants.IS_WINDOWS and not win_supported or one_drive_in_path:
|
|
838
|
+
if constants.IS_WINDOWS and (not win_supported or one_drive_in_path):
|
|
838
839
|
if not win_supported:
|
|
839
840
|
console.warn(
|
|
840
841
|
"Bun for Windows is currently only available for x86 64-bit Windows. Installation will fall back on npm."
|
|
@@ -926,7 +927,7 @@ def cached_procedure(cache_file: str, payload_fn: Callable[..., str]):
|
|
|
926
927
|
|
|
927
928
|
@cached_procedure(
|
|
928
929
|
cache_file=str(get_web_dir() / "reflex.install_frontend_packages.cached"),
|
|
929
|
-
payload_fn=lambda p, c: f"{
|
|
930
|
+
payload_fn=lambda p, c: f"{sorted(p)!r},{c.json()}",
|
|
930
931
|
)
|
|
931
932
|
def install_frontend_packages(packages: set[str], config: Config):
|
|
932
933
|
"""Installs the base and custom frontend packages.
|
|
@@ -946,9 +947,12 @@ def install_frontend_packages(packages: set[str], config: Config):
|
|
|
946
947
|
get_package_manager(on_failure_return_none=True)
|
|
947
948
|
if (
|
|
948
949
|
not constants.IS_WINDOWS
|
|
949
|
-
or
|
|
950
|
-
|
|
951
|
-
|
|
950
|
+
or (
|
|
951
|
+
constants.IS_WINDOWS
|
|
952
|
+
and (
|
|
953
|
+
is_windows_bun_supported() and not windows_check_onedrive_in_path()
|
|
954
|
+
)
|
|
955
|
+
)
|
|
952
956
|
)
|
|
953
957
|
else None
|
|
954
958
|
)
|
|
@@ -1298,7 +1302,7 @@ def fetch_app_templates(version: str) -> dict[str, Template]:
|
|
|
1298
1302
|
for tp in templates_data:
|
|
1299
1303
|
if tp["hidden"] or tp["code_url"] is None:
|
|
1300
1304
|
continue
|
|
1301
|
-
known_fields =
|
|
1305
|
+
known_fields = {f.name for f in dataclasses.fields(Template)}
|
|
1302
1306
|
filtered_templates[tp["name"]] = Template(
|
|
1303
1307
|
**{k: v for k, v in tp.items() if k in known_fields}
|
|
1304
1308
|
)
|
|
@@ -1324,7 +1328,7 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
|
|
|
1324
1328
|
raise typer.Exit(1) from ose
|
|
1325
1329
|
|
|
1326
1330
|
# Use httpx GET with redirects to download the zip file.
|
|
1327
|
-
zip_file_path = Path(temp_dir) / "template.zip"
|
|
1331
|
+
zip_file_path: Path = Path(temp_dir) / "template.zip"
|
|
1328
1332
|
try:
|
|
1329
1333
|
# Note: following redirects can be risky. We only allow this for reflex built templates at the moment.
|
|
1330
1334
|
response = net.get(template_url, follow_redirects=True)
|
|
@@ -1334,9 +1338,8 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
|
|
|
1334
1338
|
console.error(f"Failed to download the template: {he}")
|
|
1335
1339
|
raise typer.Exit(1) from he
|
|
1336
1340
|
try:
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
console.debug(f"Downloaded the zip to {zip_file_path}")
|
|
1341
|
+
zip_file_path.write_bytes(response.content)
|
|
1342
|
+
console.debug(f"Downloaded the zip to {zip_file_path}")
|
|
1340
1343
|
except OSError as ose:
|
|
1341
1344
|
console.error(f"Unable to write the downloaded zip to disk {ose}")
|
|
1342
1345
|
raise typer.Exit(1) from ose
|
reflex/utils/processes.py
CHANGED
|
@@ -58,7 +58,9 @@ def get_process_on_port(port) -> Optional[psutil.Process]:
|
|
|
58
58
|
The process on the given port.
|
|
59
59
|
"""
|
|
60
60
|
for proc in psutil.process_iter(["pid", "name", "cmdline"]):
|
|
61
|
-
|
|
61
|
+
with contextlib.suppress(
|
|
62
|
+
psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess
|
|
63
|
+
):
|
|
62
64
|
if importlib.metadata.version("psutil") >= "6.0.0":
|
|
63
65
|
conns = proc.net_connections(kind="inet") # type: ignore
|
|
64
66
|
else:
|
|
@@ -66,8 +68,6 @@ def get_process_on_port(port) -> Optional[psutil.Process]:
|
|
|
66
68
|
for conn in conns:
|
|
67
69
|
if conn.laddr.port == int(port):
|
|
68
70
|
return proc
|
|
69
|
-
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
70
|
-
pass
|
|
71
71
|
return None
|
|
72
72
|
|
|
73
73
|
|
|
@@ -364,7 +364,7 @@ def get_command_with_loglevel(command: list[str]) -> list[str]:
|
|
|
364
364
|
npm_path = str(Path(npm_path).resolve()) if npm_path else npm_path
|
|
365
365
|
|
|
366
366
|
if command[0] == npm_path:
|
|
367
|
-
return command
|
|
367
|
+
return [*command, "--loglevel", "silly"]
|
|
368
368
|
return command
|
|
369
369
|
|
|
370
370
|
|
reflex/utils/pyi_generator.py
CHANGED
|
@@ -24,7 +24,7 @@ from reflex.vars.base import Var
|
|
|
24
24
|
|
|
25
25
|
logger = logging.getLogger("pyi_generator")
|
|
26
26
|
|
|
27
|
-
PWD = Path
|
|
27
|
+
PWD = Path.cwd()
|
|
28
28
|
|
|
29
29
|
EXCLUDED_FILES = [
|
|
30
30
|
"app.py",
|
|
@@ -196,12 +196,7 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
|
|
|
196
196
|
elif isinstance(value, str):
|
|
197
197
|
ev = eval(value, type_hint_globals)
|
|
198
198
|
if rx_types.is_optional(ev):
|
|
199
|
-
# hints = {
|
|
200
|
-
# _get_type_hint(arg, type_hint_globals, is_optional=False)
|
|
201
|
-
# for arg in ev.__args__
|
|
202
|
-
# }
|
|
203
199
|
return _get_type_hint(ev, type_hint_globals, is_optional=False)
|
|
204
|
-
# return f"Optional[{', '.join(hints)}]"
|
|
205
200
|
|
|
206
201
|
if rx_types.is_union(ev):
|
|
207
202
|
res = [
|
|
@@ -260,8 +255,15 @@ def _generate_docstrings(clzs: list[Type[Component]], props: list[str]) -> str:
|
|
|
260
255
|
# We've reached the functions, so stop.
|
|
261
256
|
break
|
|
262
257
|
|
|
258
|
+
if line == "":
|
|
259
|
+
# We hit a blank line, so clear comments to avoid commented out prop appearing in next prop docs.
|
|
260
|
+
comments.clear()
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
263
|
# Get comments for prop
|
|
264
264
|
if line.strip().startswith("#"):
|
|
265
|
+
# Remove noqa from the comments.
|
|
266
|
+
line = line.partition(" # noqa")[0]
|
|
265
267
|
comments.append(line)
|
|
266
268
|
continue
|
|
267
269
|
|
|
@@ -285,10 +287,9 @@ def _generate_docstrings(clzs: list[Type[Component]], props: list[str]) -> str:
|
|
|
285
287
|
for line in (clz.create.__doc__ or "").splitlines():
|
|
286
288
|
if "**" in line:
|
|
287
289
|
indent = line.split("**")[0]
|
|
288
|
-
|
|
289
|
-
f"{indent}{n}:{' '.join(c)}" for n, c in props_comments.items()
|
|
290
|
-
|
|
291
|
-
new_docstring.append(nline)
|
|
290
|
+
new_docstring.extend(
|
|
291
|
+
[f"{indent}{n}:{' '.join(c)}" for n, c in props_comments.items()]
|
|
292
|
+
)
|
|
292
293
|
new_docstring.append(line)
|
|
293
294
|
return "\n".join(new_docstring)
|
|
294
295
|
|
|
@@ -835,7 +836,7 @@ class StubGenerator(ast.NodeTransformer):
|
|
|
835
836
|
self.inserted_imports = True
|
|
836
837
|
default_imports = _generate_imports(self.typing_imports)
|
|
837
838
|
self.import_statements.extend(ast.unparse(i) for i in default_imports)
|
|
838
|
-
return default_imports
|
|
839
|
+
return [*default_imports, node]
|
|
839
840
|
return node
|
|
840
841
|
|
|
841
842
|
def visit_ImportFrom(
|
reflex/utils/types.py
CHANGED
|
@@ -97,7 +97,6 @@ StateIterVar = Union[list, set, tuple]
|
|
|
97
97
|
if TYPE_CHECKING:
|
|
98
98
|
from reflex.vars.base import Var
|
|
99
99
|
|
|
100
|
-
# ArgsSpec = Callable[[Var], list[Var]]
|
|
101
100
|
ArgsSpec = (
|
|
102
101
|
Callable[[], Sequence[Var]]
|
|
103
102
|
| Callable[[Var], Sequence[Var]]
|
|
@@ -331,7 +330,11 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
|
|
|
331
330
|
type_ = field.outer_type_
|
|
332
331
|
if isinstance(type_, ModelField):
|
|
333
332
|
type_ = type_.type_
|
|
334
|
-
if
|
|
333
|
+
if (
|
|
334
|
+
not field.required
|
|
335
|
+
and field.default is None
|
|
336
|
+
and field.default_factory is None
|
|
337
|
+
):
|
|
335
338
|
# Ensure frontend uses null coalescing when accessing.
|
|
336
339
|
type_ = Optional[type_]
|
|
337
340
|
return type_
|
|
@@ -783,7 +786,7 @@ def validate_literal(key: str, value: Any, expected_type: Type, comp_name: str):
|
|
|
783
786
|
)
|
|
784
787
|
value_str = f"'{value}'" if isinstance(value, str) else value
|
|
785
788
|
raise ValueError(
|
|
786
|
-
f"prop value for {
|
|
789
|
+
f"prop value for {key!s} of the `{comp_name}` component should be one of the following: {allowed_value_str}. Got {value_str} instead"
|
|
787
790
|
)
|
|
788
791
|
|
|
789
792
|
|
reflex/vars/__init__.py
CHANGED
|
@@ -9,6 +9,7 @@ from .base import get_unique_variable_name as get_unique_variable_name
|
|
|
9
9
|
from .base import get_uuid_string_var as get_uuid_string_var
|
|
10
10
|
from .base import var_operation as var_operation
|
|
11
11
|
from .base import var_operation_return as var_operation_return
|
|
12
|
+
from .datetime import DateTimeVar as DateTimeVar
|
|
12
13
|
from .function import FunctionStringVar as FunctionStringVar
|
|
13
14
|
from .function import FunctionVar as FunctionVar
|
|
14
15
|
from .function import VarOperationCall as VarOperationCall
|