reflex 0.6.0a4__py3-none-any.whl → 0.6.1__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 +14 -0
- reflex/.templates/web/utils/state.js +67 -40
- reflex/app.py +9 -5
- reflex/app_mixins/lifespan.py +24 -6
- reflex/base.py +7 -13
- reflex/compiler/utils.py +17 -8
- reflex/components/base/bare.py +3 -1
- reflex/components/base/meta.py +5 -3
- reflex/components/component.py +19 -19
- reflex/components/core/cond.py +4 -4
- reflex/components/datadisplay/__init__.py +0 -1
- reflex/components/datadisplay/__init__.pyi +0 -1
- reflex/components/datadisplay/code.py +93 -106
- reflex/components/datadisplay/code.pyi +710 -53
- reflex/components/datadisplay/logo.py +22 -20
- reflex/components/dynamic.py +157 -0
- reflex/components/el/elements/forms.py +4 -1
- reflex/components/gridjs/datatable.py +2 -1
- reflex/components/markdown/markdown.py +10 -6
- reflex/components/markdown/markdown.pyi +3 -0
- reflex/components/radix/themes/components/progress.py +22 -0
- reflex/components/radix/themes/components/progress.pyi +2 -0
- reflex/components/radix/themes/components/segmented_control.py +3 -0
- reflex/components/radix/themes/components/segmented_control.pyi +2 -0
- reflex/components/radix/themes/layout/stack.py +1 -1
- reflex/components/recharts/cartesian.py +1 -1
- reflex/components/sonner/toast.py +3 -3
- reflex/components/tags/iter_tag.py +5 -1
- reflex/config.py +2 -2
- reflex/constants/base.py +4 -1
- reflex/constants/installer.py +8 -1
- reflex/event.py +61 -20
- reflex/experimental/assets.py +3 -1
- reflex/experimental/client_state.py +5 -1
- reflex/experimental/misc.py +5 -3
- reflex/middleware/hydrate_middleware.py +1 -2
- reflex/page.py +10 -3
- reflex/reflex.py +20 -3
- reflex/state.py +105 -43
- reflex/style.py +12 -2
- reflex/testing.py +8 -4
- reflex/utils/console.py +1 -1
- reflex/utils/exceptions.py +4 -0
- reflex/utils/exec.py +170 -18
- reflex/utils/format.py +6 -44
- reflex/utils/path_ops.py +36 -1
- reflex/utils/prerequisites.py +42 -14
- reflex/utils/serializers.py +7 -46
- reflex/utils/types.py +17 -2
- reflex/vars/base.py +303 -43
- reflex/vars/number.py +3 -0
- reflex/vars/sequence.py +43 -0
- {reflex-0.6.0a4.dist-info → reflex-0.6.1.dist-info}/METADATA +2 -3
- {reflex-0.6.0a4.dist-info → reflex-0.6.1.dist-info}/RECORD +57 -56
- {reflex-0.6.0a4.dist-info → reflex-0.6.1.dist-info}/LICENSE +0 -0
- {reflex-0.6.0a4.dist-info → reflex-0.6.1.dist-info}/WHEEL +0 -0
- {reflex-0.6.0a4.dist-info → reflex-0.6.1.dist-info}/entry_points.txt +0 -0
reflex/utils/exec.py
CHANGED
|
@@ -16,6 +16,7 @@ import psutil
|
|
|
16
16
|
|
|
17
17
|
from reflex import constants
|
|
18
18
|
from reflex.config import get_config
|
|
19
|
+
from reflex.constants.base import LogLevel
|
|
19
20
|
from reflex.utils import console, path_ops
|
|
20
21
|
from reflex.utils.prerequisites import get_web_dir
|
|
21
22
|
|
|
@@ -60,6 +61,13 @@ def kill(proc_pid: int):
|
|
|
60
61
|
process.kill()
|
|
61
62
|
|
|
62
63
|
|
|
64
|
+
def notify_backend():
|
|
65
|
+
"""Output a string notifying where the backend is running."""
|
|
66
|
+
console.print(
|
|
67
|
+
f"Backend running at: [bold green]http://0.0.0.0:{get_config().backend_port}[/bold green]"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
63
71
|
# run_process_and_launch_url is assumed to be used
|
|
64
72
|
# only to launch the frontend
|
|
65
73
|
# If this is not the case, might have to change the logic
|
|
@@ -103,9 +111,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
|
|
|
103
111
|
f"App running at: [bold green]{url}[/bold green]{' (Frontend-only mode)' if not backend_present else ''}"
|
|
104
112
|
)
|
|
105
113
|
if backend_present:
|
|
106
|
-
|
|
107
|
-
f"Backend running at: [bold green]http://0.0.0.0:{get_config().backend_port}[/bold green]"
|
|
108
|
-
)
|
|
114
|
+
notify_backend()
|
|
109
115
|
first_run = False
|
|
110
116
|
else:
|
|
111
117
|
console.print("New packages detected: Updating app...")
|
|
@@ -172,10 +178,42 @@ def run_frontend_prod(root: Path, port: str, backend_present=True):
|
|
|
172
178
|
)
|
|
173
179
|
|
|
174
180
|
|
|
181
|
+
def should_use_granian():
|
|
182
|
+
"""Whether to use Granian for backend.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
True if Granian should be used.
|
|
186
|
+
"""
|
|
187
|
+
return os.getenv("REFLEX_USE_GRANIAN", "0") == "1"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def get_app_module():
|
|
191
|
+
"""Get the app module for the backend.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
The app module for the backend.
|
|
195
|
+
"""
|
|
196
|
+
return f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def get_granian_target():
|
|
200
|
+
"""Get the Granian target for the backend.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
The Granian target for the backend.
|
|
204
|
+
"""
|
|
205
|
+
import reflex
|
|
206
|
+
|
|
207
|
+
app_module_path = Path(reflex.__file__).parent / "app_module_for_backend.py"
|
|
208
|
+
|
|
209
|
+
return f"{str(app_module_path)}:{constants.CompileVars.APP}.{constants.CompileVars.API}"
|
|
210
|
+
|
|
211
|
+
|
|
175
212
|
def run_backend(
|
|
176
213
|
host: str,
|
|
177
214
|
port: int,
|
|
178
215
|
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
|
|
216
|
+
frontend_present: bool = False,
|
|
179
217
|
):
|
|
180
218
|
"""Run the backend.
|
|
181
219
|
|
|
@@ -183,25 +221,82 @@ def run_backend(
|
|
|
183
221
|
host: The app host
|
|
184
222
|
port: The app port
|
|
185
223
|
loglevel: The log level.
|
|
224
|
+
frontend_present: Whether the frontend is present.
|
|
186
225
|
"""
|
|
187
|
-
import uvicorn
|
|
188
|
-
|
|
189
|
-
config = get_config()
|
|
190
|
-
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
|
191
|
-
|
|
192
226
|
web_dir = get_web_dir()
|
|
193
227
|
# Create a .nocompile file to skip compile for backend.
|
|
194
228
|
if web_dir.exists():
|
|
195
229
|
(web_dir / constants.NOCOMPILE_FILE).touch()
|
|
196
230
|
|
|
231
|
+
if not frontend_present:
|
|
232
|
+
notify_backend()
|
|
233
|
+
|
|
197
234
|
# Run the backend in development mode.
|
|
235
|
+
if should_use_granian():
|
|
236
|
+
run_granian_backend(host, port, loglevel)
|
|
237
|
+
else:
|
|
238
|
+
run_uvicorn_backend(host, port, loglevel)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def run_uvicorn_backend(host, port, loglevel: LogLevel):
|
|
242
|
+
"""Run the backend in development mode using Uvicorn.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
host: The app host
|
|
246
|
+
port: The app port
|
|
247
|
+
loglevel: The log level.
|
|
248
|
+
"""
|
|
249
|
+
import uvicorn
|
|
250
|
+
|
|
198
251
|
uvicorn.run(
|
|
199
|
-
app=f"{
|
|
252
|
+
app=f"{get_app_module()}.{constants.CompileVars.API}",
|
|
200
253
|
host=host,
|
|
201
254
|
port=port,
|
|
202
255
|
log_level=loglevel.value,
|
|
203
256
|
reload=True,
|
|
204
|
-
reload_dirs=[
|
|
257
|
+
reload_dirs=[get_config().app_name],
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def run_granian_backend(host, port, loglevel: LogLevel):
|
|
262
|
+
"""Run the backend in development mode using Granian.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
host: The app host
|
|
266
|
+
port: The app port
|
|
267
|
+
loglevel: The log level.
|
|
268
|
+
"""
|
|
269
|
+
console.debug("Using Granian for backend")
|
|
270
|
+
try:
|
|
271
|
+
from granian import Granian # type: ignore
|
|
272
|
+
from granian.constants import Interfaces # type: ignore
|
|
273
|
+
from granian.log import LogLevels # type: ignore
|
|
274
|
+
|
|
275
|
+
Granian(
|
|
276
|
+
target=get_granian_target(),
|
|
277
|
+
address=host,
|
|
278
|
+
port=port,
|
|
279
|
+
interface=Interfaces.ASGI,
|
|
280
|
+
log_level=LogLevels(loglevel.value),
|
|
281
|
+
reload=True,
|
|
282
|
+
reload_paths=[Path(get_config().app_name)],
|
|
283
|
+
reload_ignore_dirs=[".web"],
|
|
284
|
+
).serve()
|
|
285
|
+
except ImportError:
|
|
286
|
+
console.error(
|
|
287
|
+
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian>=1.6.0"`)'
|
|
288
|
+
)
|
|
289
|
+
os._exit(1)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _get_backend_workers():
|
|
293
|
+
from reflex.utils import processes
|
|
294
|
+
|
|
295
|
+
config = get_config()
|
|
296
|
+
return (
|
|
297
|
+
processes.get_num_workers()
|
|
298
|
+
if not config.gunicorn_workers
|
|
299
|
+
else config.gunicorn_workers
|
|
205
300
|
)
|
|
206
301
|
|
|
207
302
|
|
|
@@ -209,9 +304,28 @@ def run_backend_prod(
|
|
|
209
304
|
host: str,
|
|
210
305
|
port: int,
|
|
211
306
|
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
|
|
307
|
+
frontend_present: bool = False,
|
|
212
308
|
):
|
|
213
309
|
"""Run the backend.
|
|
214
310
|
|
|
311
|
+
Args:
|
|
312
|
+
host: The app host
|
|
313
|
+
port: The app port
|
|
314
|
+
loglevel: The log level.
|
|
315
|
+
frontend_present: Whether the frontend is present.
|
|
316
|
+
"""
|
|
317
|
+
if not frontend_present:
|
|
318
|
+
notify_backend()
|
|
319
|
+
|
|
320
|
+
if should_use_granian():
|
|
321
|
+
run_granian_backend_prod(host, port, loglevel)
|
|
322
|
+
else:
|
|
323
|
+
run_uvicorn_backend_prod(host, port, loglevel)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def run_uvicorn_backend_prod(host, port, loglevel):
|
|
327
|
+
"""Run the backend in production mode using Uvicorn.
|
|
328
|
+
|
|
215
329
|
Args:
|
|
216
330
|
host: The app host
|
|
217
331
|
port: The app port
|
|
@@ -220,14 +334,11 @@ def run_backend_prod(
|
|
|
220
334
|
from reflex.utils import processes
|
|
221
335
|
|
|
222
336
|
config = get_config()
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
else config.gunicorn_workers
|
|
227
|
-
)
|
|
337
|
+
|
|
338
|
+
app_module = get_app_module()
|
|
339
|
+
|
|
228
340
|
RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --preload --timeout {config.timeout} --log-level critical".split()
|
|
229
341
|
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
|
|
230
|
-
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
|
231
342
|
command = (
|
|
232
343
|
[
|
|
233
344
|
*RUN_BACKEND_PROD_WINDOWS,
|
|
@@ -243,7 +354,7 @@ def run_backend_prod(
|
|
|
243
354
|
"--bind",
|
|
244
355
|
f"{host}:{port}",
|
|
245
356
|
"--threads",
|
|
246
|
-
str(
|
|
357
|
+
str(_get_backend_workers()),
|
|
247
358
|
f"{app_module}()",
|
|
248
359
|
]
|
|
249
360
|
)
|
|
@@ -252,7 +363,7 @@ def run_backend_prod(
|
|
|
252
363
|
"--log-level",
|
|
253
364
|
loglevel.value,
|
|
254
365
|
"--workers",
|
|
255
|
-
str(
|
|
366
|
+
str(_get_backend_workers()),
|
|
256
367
|
]
|
|
257
368
|
processes.new_process(
|
|
258
369
|
command,
|
|
@@ -262,6 +373,47 @@ def run_backend_prod(
|
|
|
262
373
|
)
|
|
263
374
|
|
|
264
375
|
|
|
376
|
+
def run_granian_backend_prod(host, port, loglevel):
|
|
377
|
+
"""Run the backend in production mode using Granian.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
host: The app host
|
|
381
|
+
port: The app port
|
|
382
|
+
loglevel: The log level.
|
|
383
|
+
"""
|
|
384
|
+
from reflex.utils import processes
|
|
385
|
+
|
|
386
|
+
try:
|
|
387
|
+
from granian.constants import Interfaces # type: ignore
|
|
388
|
+
|
|
389
|
+
command = [
|
|
390
|
+
"granian",
|
|
391
|
+
"--workers",
|
|
392
|
+
str(_get_backend_workers()),
|
|
393
|
+
"--log-level",
|
|
394
|
+
"critical",
|
|
395
|
+
"--host",
|
|
396
|
+
host,
|
|
397
|
+
"--port",
|
|
398
|
+
str(port),
|
|
399
|
+
"--interface",
|
|
400
|
+
str(Interfaces.ASGI),
|
|
401
|
+
get_granian_target(),
|
|
402
|
+
]
|
|
403
|
+
processes.new_process(
|
|
404
|
+
command,
|
|
405
|
+
run=True,
|
|
406
|
+
show_logs=True,
|
|
407
|
+
env={
|
|
408
|
+
constants.SKIP_COMPILE_ENV_VAR: "yes"
|
|
409
|
+
}, # skip compile for prod backend
|
|
410
|
+
)
|
|
411
|
+
except ImportError:
|
|
412
|
+
console.error(
|
|
413
|
+
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian>=1.6.0"`)'
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
|
|
265
417
|
def output_system_info():
|
|
266
418
|
"""Show system information if the loglevel is in DEBUG."""
|
|
267
419
|
if console._LOG_LEVEL > constants.LogLevel.DEBUG:
|
reflex/utils/format.py
CHANGED
|
@@ -9,7 +9,7 @@ import re
|
|
|
9
9
|
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union
|
|
10
10
|
|
|
11
11
|
from reflex import constants
|
|
12
|
-
from reflex.utils import exceptions
|
|
12
|
+
from reflex.utils import exceptions
|
|
13
13
|
from reflex.utils.console import deprecate
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
@@ -345,6 +345,7 @@ def format_prop(
|
|
|
345
345
|
Raises:
|
|
346
346
|
exceptions.InvalidStylePropError: If the style prop value is not a valid type.
|
|
347
347
|
TypeError: If the prop is not valid.
|
|
348
|
+
ValueError: If the prop is not a string.
|
|
348
349
|
"""
|
|
349
350
|
# import here to avoid circular import.
|
|
350
351
|
from reflex.event import EventChain
|
|
@@ -391,7 +392,8 @@ def format_prop(
|
|
|
391
392
|
raise TypeError(f"Could not format prop: {prop} of type {type(prop)}") from e
|
|
392
393
|
|
|
393
394
|
# Wrap the variable in braces.
|
|
394
|
-
|
|
395
|
+
if not isinstance(prop, str):
|
|
396
|
+
raise ValueError(f"Invalid prop: {prop}. Expected a string.")
|
|
395
397
|
return wrap(prop, "{", check_first=False)
|
|
396
398
|
|
|
397
399
|
|
|
@@ -624,48 +626,6 @@ def format_query_params(router_data: dict[str, Any]) -> dict[str, str]:
|
|
|
624
626
|
return {k.replace("-", "_"): v for k, v in params.items()}
|
|
625
627
|
|
|
626
628
|
|
|
627
|
-
def format_state(value: Any, key: Optional[str] = None) -> Any:
|
|
628
|
-
"""Recursively format values in the given state.
|
|
629
|
-
|
|
630
|
-
Args:
|
|
631
|
-
value: The state to format.
|
|
632
|
-
key: The key associated with the value (optional).
|
|
633
|
-
|
|
634
|
-
Returns:
|
|
635
|
-
The formatted state.
|
|
636
|
-
|
|
637
|
-
Raises:
|
|
638
|
-
TypeError: If the given value is not a valid state.
|
|
639
|
-
"""
|
|
640
|
-
from reflex.utils import serializers
|
|
641
|
-
|
|
642
|
-
# Handle dicts.
|
|
643
|
-
if isinstance(value, dict):
|
|
644
|
-
return {k: format_state(v, k) for k, v in value.items()}
|
|
645
|
-
|
|
646
|
-
# Handle lists, sets, typles.
|
|
647
|
-
if isinstance(value, types.StateIterBases):
|
|
648
|
-
return [format_state(v) for v in value]
|
|
649
|
-
|
|
650
|
-
# Return state vars as is.
|
|
651
|
-
if isinstance(value, types.StateBases):
|
|
652
|
-
return value
|
|
653
|
-
|
|
654
|
-
# Serialize the value.
|
|
655
|
-
serialized = serializers.serialize(value)
|
|
656
|
-
if serialized is not None:
|
|
657
|
-
return serialized
|
|
658
|
-
|
|
659
|
-
if key is None:
|
|
660
|
-
raise TypeError(
|
|
661
|
-
f"No JSON serializer found for var {value} of type {type(value)}."
|
|
662
|
-
)
|
|
663
|
-
else:
|
|
664
|
-
raise TypeError(
|
|
665
|
-
f"No JSON serializer found for State Var '{key}' of value {value} of type {type(value)}."
|
|
666
|
-
)
|
|
667
|
-
|
|
668
|
-
|
|
669
629
|
def format_state_name(state_name: str) -> str:
|
|
670
630
|
"""Format a state name, replacing dots with double underscore.
|
|
671
631
|
|
|
@@ -704,6 +664,8 @@ def format_library_name(library_fullname: str):
|
|
|
704
664
|
Returns:
|
|
705
665
|
The name without the @version if it was part of the name
|
|
706
666
|
"""
|
|
667
|
+
if library_fullname.startswith("https://"):
|
|
668
|
+
return library_fullname
|
|
707
669
|
lib, at, version = library_fullname.rpartition("@")
|
|
708
670
|
if not lib:
|
|
709
671
|
lib = at + version
|
reflex/utils/path_ops.py
CHANGED
|
@@ -129,6 +129,41 @@ def which(program: str | Path) -> str | Path | None:
|
|
|
129
129
|
return shutil.which(str(program))
|
|
130
130
|
|
|
131
131
|
|
|
132
|
+
def use_system_install(var_name: str) -> bool:
|
|
133
|
+
"""Check if the system install should be used.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
var_name: The name of the environment variable.
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
ValueError: If the variable name is invalid.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Whether the associated env var should use the system install.
|
|
143
|
+
"""
|
|
144
|
+
if not var_name.startswith("REFLEX_USE_SYSTEM_"):
|
|
145
|
+
raise ValueError("Invalid system install variable name.")
|
|
146
|
+
return os.getenv(var_name, "").lower() in ["true", "1", "yes"]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def use_system_node() -> bool:
|
|
150
|
+
"""Check if the system node should be used.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Whether the system node should be used.
|
|
154
|
+
"""
|
|
155
|
+
return use_system_install(constants.Node.USE_SYSTEM_VAR)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def use_system_bun() -> bool:
|
|
159
|
+
"""Check if the system bun should be used.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Whether the system bun should be used.
|
|
163
|
+
"""
|
|
164
|
+
return use_system_install(constants.Bun.USE_SYSTEM_VAR)
|
|
165
|
+
|
|
166
|
+
|
|
132
167
|
def get_node_bin_path() -> str | None:
|
|
133
168
|
"""Get the node binary dir path.
|
|
134
169
|
|
|
@@ -149,7 +184,7 @@ def get_node_path() -> str | None:
|
|
|
149
184
|
The path to the node binary file.
|
|
150
185
|
"""
|
|
151
186
|
node_path = Path(constants.Node.PATH)
|
|
152
|
-
if not node_path.exists():
|
|
187
|
+
if use_system_node() or not node_path.exists():
|
|
153
188
|
return str(which("node"))
|
|
154
189
|
return str(node_path)
|
|
155
190
|
|
reflex/utils/prerequisites.py
CHANGED
|
@@ -16,7 +16,7 @@ import shutil
|
|
|
16
16
|
import stat
|
|
17
17
|
import sys
|
|
18
18
|
import tempfile
|
|
19
|
-
import
|
|
19
|
+
import time
|
|
20
20
|
import zipfile
|
|
21
21
|
from datetime import datetime
|
|
22
22
|
from fileinput import FileInput
|
|
@@ -36,6 +36,7 @@ from reflex import constants, model
|
|
|
36
36
|
from reflex.compiler import templates
|
|
37
37
|
from reflex.config import Config, get_config
|
|
38
38
|
from reflex.utils import console, net, path_ops, processes
|
|
39
|
+
from reflex.utils.exceptions import GeneratedCodeHasNoFunctionDefs
|
|
39
40
|
from reflex.utils.format import format_library_name
|
|
40
41
|
from reflex.utils.registry import _get_best_registry
|
|
41
42
|
|
|
@@ -142,7 +143,7 @@ def check_node_version() -> bool:
|
|
|
142
143
|
# Compare the version numbers
|
|
143
144
|
return (
|
|
144
145
|
current_version >= version.parse(constants.Node.MIN_VERSION)
|
|
145
|
-
if constants.IS_WINDOWS
|
|
146
|
+
if constants.IS_WINDOWS or path_ops.use_system_node()
|
|
146
147
|
else current_version == version.parse(constants.Node.VERSION)
|
|
147
148
|
)
|
|
148
149
|
return False
|
|
@@ -1033,6 +1034,8 @@ def validate_bun():
|
|
|
1033
1034
|
# if a custom bun path is provided, make sure its valid
|
|
1034
1035
|
# This is specific to non-FHS OS
|
|
1035
1036
|
bun_path = get_config().bun_path
|
|
1037
|
+
if path_ops.use_system_bun():
|
|
1038
|
+
bun_path = path_ops.which("bun")
|
|
1036
1039
|
if bun_path != constants.Bun.DEFAULT_PATH:
|
|
1037
1040
|
console.info(f"Using custom Bun path: {bun_path}")
|
|
1038
1041
|
bun_version = get_bun_version()
|
|
@@ -1455,19 +1458,37 @@ def initialize_main_module_index_from_generation(app_name: str, generation_hash:
|
|
|
1455
1458
|
Args:
|
|
1456
1459
|
app_name: The name of the app.
|
|
1457
1460
|
generation_hash: The generation hash from reflex.build.
|
|
1461
|
+
|
|
1462
|
+
Raises:
|
|
1463
|
+
GeneratedCodeHasNoFunctionDefs: If the fetched code has no function definitions
|
|
1464
|
+
(the refactored reflex code is expected to have at least one root function defined).
|
|
1458
1465
|
"""
|
|
1459
1466
|
# Download the reflex code for the generation.
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1467
|
+
url = constants.Templates.REFLEX_BUILD_CODE_URL.format(
|
|
1468
|
+
generation_hash=generation_hash
|
|
1469
|
+
)
|
|
1470
|
+
resp = net.get(url)
|
|
1471
|
+
while resp.status_code == httpx.codes.SERVICE_UNAVAILABLE:
|
|
1472
|
+
console.debug("Waiting for the code to be generated...")
|
|
1473
|
+
time.sleep(1)
|
|
1474
|
+
resp = net.get(url)
|
|
1475
|
+
resp.raise_for_status()
|
|
1476
|
+
|
|
1477
|
+
# Determine the name of the last function, which renders the generated code.
|
|
1478
|
+
defined_funcs = re.findall(r"def ([a-zA-Z_]+)\(", resp.text)
|
|
1479
|
+
if not defined_funcs:
|
|
1480
|
+
raise GeneratedCodeHasNoFunctionDefs(
|
|
1481
|
+
f"No function definitions found in generated code from {url!r}."
|
|
1463
1482
|
)
|
|
1464
|
-
|
|
1483
|
+
render_func_name = defined_funcs[-1]
|
|
1465
1484
|
|
|
1466
1485
|
def replace_content(_match):
|
|
1467
1486
|
return "\n".join(
|
|
1468
1487
|
[
|
|
1469
|
-
|
|
1470
|
-
|
|
1488
|
+
resp.text,
|
|
1489
|
+
"",
|
|
1490
|
+
"" "def index() -> rx.Component:",
|
|
1491
|
+
f" return {render_func_name}()",
|
|
1471
1492
|
"",
|
|
1472
1493
|
"",
|
|
1473
1494
|
],
|
|
@@ -1475,13 +1496,20 @@ def initialize_main_module_index_from_generation(app_name: str, generation_hash:
|
|
|
1475
1496
|
|
|
1476
1497
|
main_module_path = Path(app_name, app_name + constants.Ext.PY)
|
|
1477
1498
|
main_module_code = main_module_path.read_text()
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1499
|
+
|
|
1500
|
+
main_module_code = re.sub(
|
|
1501
|
+
r"def index\(\).*:\n([^\n]\s+.*\n+)+",
|
|
1502
|
+
replace_content,
|
|
1503
|
+
main_module_code,
|
|
1504
|
+
)
|
|
1505
|
+
# Make the app use light mode until flexgen enforces the conversion of
|
|
1506
|
+
# tailwind colors to radix colors.
|
|
1507
|
+
main_module_code = re.sub(
|
|
1508
|
+
r"app\s*=\s*rx\.App\(\s*\)",
|
|
1509
|
+
'app = rx.App(theme=rx.theme(color_mode="light"))',
|
|
1510
|
+
main_module_code,
|
|
1484
1511
|
)
|
|
1512
|
+
main_module_path.write_text(main_module_code)
|
|
1485
1513
|
|
|
1486
1514
|
|
|
1487
1515
|
def format_address_width(address_width) -> int | None:
|
reflex/utils/serializers.py
CHANGED
|
@@ -12,7 +12,6 @@ from pathlib import Path
|
|
|
12
12
|
from typing import (
|
|
13
13
|
Any,
|
|
14
14
|
Callable,
|
|
15
|
-
Dict,
|
|
16
15
|
List,
|
|
17
16
|
Literal,
|
|
18
17
|
Optional,
|
|
@@ -126,7 +125,8 @@ def serialize(
|
|
|
126
125
|
# If there is no serializer, return None.
|
|
127
126
|
if serializer is None:
|
|
128
127
|
if dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
129
|
-
return
|
|
128
|
+
return {k.name: getattr(value, k.name) for k in dataclasses.fields(value)}
|
|
129
|
+
|
|
130
130
|
if get_type:
|
|
131
131
|
return None, None
|
|
132
132
|
return None
|
|
@@ -214,32 +214,6 @@ def serialize_type(value: type) -> str:
|
|
|
214
214
|
return value.__name__
|
|
215
215
|
|
|
216
216
|
|
|
217
|
-
@serializer
|
|
218
|
-
def serialize_str(value: str) -> str:
|
|
219
|
-
"""Serialize a string.
|
|
220
|
-
|
|
221
|
-
Args:
|
|
222
|
-
value: The string to serialize.
|
|
223
|
-
|
|
224
|
-
Returns:
|
|
225
|
-
The serialized string.
|
|
226
|
-
"""
|
|
227
|
-
return value
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
@serializer
|
|
231
|
-
def serialize_primitive(value: Union[bool, int, float, None]):
|
|
232
|
-
"""Serialize a primitive type.
|
|
233
|
-
|
|
234
|
-
Args:
|
|
235
|
-
value: The number/bool/None to serialize.
|
|
236
|
-
|
|
237
|
-
Returns:
|
|
238
|
-
The serialized number/bool/None.
|
|
239
|
-
"""
|
|
240
|
-
return value
|
|
241
|
-
|
|
242
|
-
|
|
243
217
|
@serializer
|
|
244
218
|
def serialize_base(value: Base) -> dict:
|
|
245
219
|
"""Serialize a Base instance.
|
|
@@ -250,33 +224,20 @@ def serialize_base(value: Base) -> dict:
|
|
|
250
224
|
Returns:
|
|
251
225
|
The serialized Base.
|
|
252
226
|
"""
|
|
253
|
-
return {k:
|
|
227
|
+
return {k: v for k, v in value.dict().items() if not callable(v)}
|
|
254
228
|
|
|
255
229
|
|
|
256
230
|
@serializer
|
|
257
|
-
def
|
|
258
|
-
"""Serialize a
|
|
231
|
+
def serialize_set(value: Set) -> list:
|
|
232
|
+
"""Serialize a set to a JSON serializable list.
|
|
259
233
|
|
|
260
234
|
Args:
|
|
261
|
-
value: The
|
|
235
|
+
value: The set to serialize.
|
|
262
236
|
|
|
263
237
|
Returns:
|
|
264
238
|
The serialized list.
|
|
265
239
|
"""
|
|
266
|
-
return
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
@serializer
|
|
270
|
-
def serialize_dict(prop: Dict[str, Any]) -> dict:
|
|
271
|
-
"""Serialize a dictionary to a JSON string.
|
|
272
|
-
|
|
273
|
-
Args:
|
|
274
|
-
prop: The dictionary to serialize.
|
|
275
|
-
|
|
276
|
-
Returns:
|
|
277
|
-
The serialized dictionary.
|
|
278
|
-
"""
|
|
279
|
-
return {k: serialize(v) for k, v in prop.items()}
|
|
240
|
+
return list(value)
|
|
280
241
|
|
|
281
242
|
|
|
282
243
|
@serializer(to=str)
|
reflex/utils/types.py
CHANGED
|
@@ -9,6 +9,7 @@ import sys
|
|
|
9
9
|
import types
|
|
10
10
|
from functools import cached_property, lru_cache, wraps
|
|
11
11
|
from typing import (
|
|
12
|
+
TYPE_CHECKING,
|
|
12
13
|
Any,
|
|
13
14
|
Callable,
|
|
14
15
|
ClassVar,
|
|
@@ -96,8 +97,22 @@ PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
|
|
|
96
97
|
StateVar = Union[PrimitiveType, Base, None]
|
|
97
98
|
StateIterVar = Union[list, set, tuple]
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
if TYPE_CHECKING:
|
|
101
|
+
from reflex.vars.base import Var
|
|
102
|
+
|
|
103
|
+
# ArgsSpec = Callable[[Var], list[Var]]
|
|
104
|
+
ArgsSpec = (
|
|
105
|
+
Callable[[], List[Var]]
|
|
106
|
+
| Callable[[Var], List[Var]]
|
|
107
|
+
| Callable[[Var, Var], List[Var]]
|
|
108
|
+
| Callable[[Var, Var, Var], List[Var]]
|
|
109
|
+
| Callable[[Var, Var, Var, Var], List[Var]]
|
|
110
|
+
| Callable[[Var, Var, Var, Var, Var], List[Var]]
|
|
111
|
+
| Callable[[Var, Var, Var, Var, Var, Var], List[Var]]
|
|
112
|
+
| Callable[[Var, Var, Var, Var, Var, Var, Var], List[Var]]
|
|
113
|
+
)
|
|
114
|
+
else:
|
|
115
|
+
ArgsSpec = Callable[..., List[Any]]
|
|
101
116
|
|
|
102
117
|
|
|
103
118
|
PrimitiveToAnnotation = {
|