reflex 0.7.13a2__py3-none-any.whl → 0.7.14a1__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/apps/blank/code/blank.py +0 -2
- reflex/app.py +64 -69
- reflex/app_mixins/lifespan.py +2 -3
- reflex/app_mixins/middleware.py +1 -0
- reflex/app_mixins/mixin.py +0 -1
- reflex/assets.py +6 -3
- reflex/base.py +3 -2
- reflex/compiler/compiler.py +77 -64
- reflex/compiler/utils.py +8 -6
- reflex/components/base/app_wrap.pyi +0 -1
- reflex/components/base/bare.py +5 -7
- reflex/components/base/body.pyi +0 -1
- reflex/components/base/document.pyi +0 -5
- reflex/components/base/error_boundary.pyi +0 -1
- reflex/components/base/fragment.pyi +0 -1
- reflex/components/base/head.pyi +0 -2
- reflex/components/base/link.pyi +0 -2
- reflex/components/base/meta.py +2 -1
- reflex/components/base/meta.pyi +0 -4
- reflex/components/base/script.py +2 -1
- reflex/components/base/script.pyi +0 -1
- reflex/components/base/strict_mode.pyi +0 -1
- reflex/components/component.py +38 -40
- reflex/components/core/auto_scroll.pyi +0 -1
- reflex/components/core/banner.pyi +0 -6
- reflex/components/core/breakpoints.py +9 -11
- reflex/components/core/client_side_routing.pyi +0 -2
- reflex/components/core/clipboard.pyi +0 -1
- reflex/components/core/colors.py +10 -7
- reflex/components/core/cond.py +4 -2
- reflex/components/core/debounce.py +5 -3
- reflex/components/core/debounce.pyi +0 -1
- reflex/components/core/foreach.py +8 -6
- reflex/components/core/html.py +3 -3
- reflex/components/core/html.pyi +0 -1
- reflex/components/core/match.py +19 -17
- reflex/components/core/sticky.pyi +0 -4
- reflex/components/core/upload.pyi +0 -5
- reflex/components/datadisplay/code.py +1 -2
- reflex/components/datadisplay/code.pyi +0 -2
- reflex/components/datadisplay/dataeditor.py +7 -10
- reflex/components/datadisplay/dataeditor.pyi +0 -1
- reflex/components/datadisplay/logo.py +3 -4
- reflex/components/datadisplay/shiki_code_block.py +8 -11
- reflex/components/datadisplay/shiki_code_block.pyi +0 -3
- reflex/components/dynamic.py +2 -3
- reflex/components/el/__init__.pyi +2 -0
- reflex/components/el/element.pyi +0 -1
- reflex/components/el/elements/__init__.py +1 -0
- reflex/components/el/elements/__init__.pyi +3 -0
- reflex/components/el/elements/base.pyi +0 -1
- reflex/components/el/elements/forms.py +3 -4
- reflex/components/el/elements/forms.pyi +1 -18
- reflex/components/el/elements/inline.pyi +0 -28
- reflex/components/el/elements/media.py +26 -0
- reflex/components/el/elements/media.pyi +259 -25
- reflex/components/el/elements/metadata.py +0 -1
- reflex/components/el/elements/metadata.pyi +0 -6
- reflex/components/el/elements/other.pyi +0 -7
- reflex/components/el/elements/scripts.pyi +0 -3
- reflex/components/el/elements/sectioning.pyi +0 -15
- reflex/components/el/elements/tables.pyi +0 -10
- reflex/components/el/elements/typography.pyi +0 -15
- reflex/components/gridjs/datatable.py +10 -13
- reflex/components/gridjs/datatable.pyi +0 -2
- reflex/components/lucide/icon.py +10 -9
- reflex/components/lucide/icon.pyi +0 -3
- reflex/components/markdown/markdown.py +6 -8
- reflex/components/markdown/markdown.pyi +0 -1
- reflex/components/moment/moment.pyi +0 -1
- reflex/components/next/base.py +0 -2
- reflex/components/next/base.pyi +0 -3
- reflex/components/next/image.pyi +0 -1
- reflex/components/next/link.pyi +0 -1
- reflex/components/next/video.pyi +0 -1
- reflex/components/plotly/plotly.pyi +0 -9
- reflex/components/props.py +4 -3
- reflex/components/radix/primitives/accordion.pyi +0 -7
- reflex/components/radix/primitives/base.py +1 -3
- reflex/components/radix/primitives/base.pyi +0 -2
- reflex/components/radix/primitives/drawer.pyi +0 -11
- reflex/components/radix/primitives/form.py +4 -8
- reflex/components/radix/primitives/form.pyi +0 -12
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/progress.pyi +0 -5
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/primitives/slider.pyi +0 -5
- reflex/components/radix/themes/base.pyi +0 -8
- reflex/components/radix/themes/color_mode.pyi +0 -3
- reflex/components/radix/themes/components/alert_dialog.py +4 -2
- reflex/components/radix/themes/components/alert_dialog.pyi +4 -9
- reflex/components/radix/themes/components/aspect_ratio.py +1 -2
- reflex/components/radix/themes/components/aspect_ratio.pyi +1 -3
- reflex/components/radix/themes/components/avatar.py +5 -2
- reflex/components/radix/themes/components/avatar.pyi +1 -3
- reflex/components/radix/themes/components/badge.py +5 -2
- reflex/components/radix/themes/components/badge.pyi +1 -3
- reflex/components/radix/themes/components/button.py +2 -3
- reflex/components/radix/themes/components/button.pyi +1 -3
- reflex/components/radix/themes/components/callout.py +1 -2
- reflex/components/radix/themes/components/callout.pyi +1 -7
- reflex/components/radix/themes/components/card.py +1 -2
- reflex/components/radix/themes/components/card.pyi +1 -3
- reflex/components/radix/themes/components/checkbox.py +7 -4
- reflex/components/radix/themes/components/checkbox.pyi +1 -5
- reflex/components/radix/themes/components/checkbox_cards.py +1 -2
- reflex/components/radix/themes/components/checkbox_cards.pyi +1 -4
- reflex/components/radix/themes/components/checkbox_group.py +1 -2
- reflex/components/radix/themes/components/checkbox_group.pyi +1 -4
- reflex/components/radix/themes/components/context_menu.py +1 -1
- reflex/components/radix/themes/components/context_menu.pyi +1 -14
- reflex/components/radix/themes/components/data_list.py +1 -2
- reflex/components/radix/themes/components/data_list.pyi +1 -6
- reflex/components/radix/themes/components/dialog.py +4 -2
- reflex/components/radix/themes/components/dialog.pyi +4 -9
- reflex/components/radix/themes/components/dropdown_menu.py +5 -2
- reflex/components/radix/themes/components/dropdown_menu.pyi +4 -10
- reflex/components/radix/themes/components/hover_card.py +4 -2
- reflex/components/radix/themes/components/hover_card.pyi +4 -6
- reflex/components/radix/themes/components/icon_button.py +7 -8
- reflex/components/radix/themes/components/icon_button.pyi +1 -3
- reflex/components/radix/themes/components/inset.py +1 -2
- reflex/components/radix/themes/components/inset.pyi +1 -3
- reflex/components/radix/themes/components/popover.py +4 -2
- reflex/components/radix/themes/components/popover.pyi +4 -6
- reflex/components/radix/themes/components/progress.py +1 -2
- reflex/components/radix/themes/components/progress.pyi +1 -3
- reflex/components/radix/themes/components/radio.py +1 -2
- reflex/components/radix/themes/components/radio.pyi +1 -3
- reflex/components/radix/themes/components/radio_cards.py +1 -2
- reflex/components/radix/themes/components/radio_cards.pyi +1 -4
- reflex/components/radix/themes/components/radio_group.py +7 -5
- reflex/components/radix/themes/components/radio_group.pyi +1 -6
- reflex/components/radix/themes/components/scroll_area.py +1 -2
- reflex/components/radix/themes/components/scroll_area.pyi +1 -3
- reflex/components/radix/themes/components/segmented_control.py +1 -2
- reflex/components/radix/themes/components/segmented_control.pyi +1 -4
- reflex/components/radix/themes/components/select.py +5 -2
- reflex/components/radix/themes/components/select.pyi +1 -11
- reflex/components/radix/themes/components/separator.py +1 -2
- reflex/components/radix/themes/components/separator.pyi +1 -3
- reflex/components/radix/themes/components/skeleton.py +1 -2
- reflex/components/radix/themes/components/skeleton.pyi +1 -3
- reflex/components/radix/themes/components/slider.py +1 -2
- reflex/components/radix/themes/components/slider.pyi +1 -3
- reflex/components/radix/themes/components/spinner.py +1 -2
- reflex/components/radix/themes/components/spinner.pyi +1 -3
- reflex/components/radix/themes/components/switch.py +1 -2
- reflex/components/radix/themes/components/switch.pyi +1 -3
- reflex/components/radix/themes/components/table.py +1 -2
- reflex/components/radix/themes/components/table.pyi +1 -9
- reflex/components/radix/themes/components/tabs.py +1 -2
- reflex/components/radix/themes/components/tabs.pyi +1 -7
- reflex/components/radix/themes/components/text_area.py +5 -2
- reflex/components/radix/themes/components/text_area.pyi +1 -3
- reflex/components/radix/themes/components/text_field.py +5 -2
- reflex/components/radix/themes/components/text_field.pyi +1 -5
- reflex/components/radix/themes/components/tooltip.py +1 -2
- reflex/components/radix/themes/components/tooltip.pyi +1 -3
- reflex/components/radix/themes/layout/base.py +5 -2
- reflex/components/radix/themes/layout/base.pyi +5 -3
- reflex/components/radix/themes/layout/box.py +1 -2
- reflex/components/radix/themes/layout/box.pyi +1 -3
- reflex/components/radix/themes/layout/center.pyi +0 -1
- reflex/components/radix/themes/layout/container.py +1 -2
- reflex/components/radix/themes/layout/container.pyi +1 -3
- reflex/components/radix/themes/layout/flex.py +6 -2
- reflex/components/radix/themes/layout/flex.pyi +1 -3
- reflex/components/radix/themes/layout/grid.py +6 -2
- reflex/components/radix/themes/layout/grid.pyi +1 -3
- reflex/components/radix/themes/layout/list.py +2 -1
- reflex/components/radix/themes/layout/list.pyi +0 -5
- reflex/components/radix/themes/layout/section.py +1 -2
- reflex/components/radix/themes/layout/section.pyi +1 -3
- reflex/components/radix/themes/layout/spacer.pyi +0 -1
- reflex/components/radix/themes/layout/stack.py +1 -1
- reflex/components/radix/themes/layout/stack.pyi +0 -3
- reflex/components/radix/themes/typography/blockquote.py +1 -1
- reflex/components/radix/themes/typography/blockquote.pyi +1 -3
- reflex/components/radix/themes/typography/code.py +5 -1
- reflex/components/radix/themes/typography/code.pyi +1 -3
- reflex/components/radix/themes/typography/heading.py +1 -1
- reflex/components/radix/themes/typography/heading.pyi +1 -3
- reflex/components/radix/themes/typography/link.py +3 -2
- reflex/components/radix/themes/typography/link.pyi +1 -3
- reflex/components/radix/themes/typography/text.py +1 -1
- reflex/components/radix/themes/typography/text.pyi +1 -9
- reflex/components/react_player/audio.py +0 -2
- reflex/components/react_player/audio.pyi +0 -3
- reflex/components/react_player/react_player.pyi +0 -1
- reflex/components/react_player/video.py +0 -2
- reflex/components/react_player/video.pyi +0 -3
- reflex/components/recharts/__init__.py +1 -1
- reflex/components/recharts/__init__.pyi +1 -1
- reflex/components/recharts/cartesian.py +20 -25
- reflex/components/recharts/cartesian.pyi +20 -37
- reflex/components/recharts/charts.py +2 -1
- reflex/components/recharts/charts.pyi +0 -12
- reflex/components/recharts/general.pyi +0 -6
- reflex/components/recharts/polar.py +5 -4
- reflex/components/recharts/polar.pyi +4 -10
- reflex/components/recharts/recharts.py +12 -10
- reflex/components/recharts/recharts.pyi +10 -11
- reflex/components/sonner/toast.py +2 -2
- reflex/components/sonner/toast.pyi +0 -2
- reflex/components/suneditor/editor.py +2 -1
- reflex/components/suneditor/editor.pyi +0 -1
- reflex/components/tags/iter_tag.py +4 -2
- reflex/config.py +47 -35
- reflex/constants/base.py +3 -3
- reflex/constants/compiler.py +8 -6
- reflex/constants/installer.py +24 -15
- reflex/custom_components/custom_components.py +1 -2
- reflex/event.py +58 -60
- reflex/experimental/__init__.py +2 -2
- reflex/experimental/client_state.py +9 -4
- reflex/experimental/layout.pyi +0 -5
- reflex/istate/manager.py +15 -19
- reflex/istate/proxy.py +19 -12
- reflex/model.py +6 -4
- reflex/plugins/base.py +8 -0
- reflex/plugins/tailwind_v3.py +8 -0
- reflex/plugins/tailwind_v4.py +8 -0
- reflex/reflex.py +9 -11
- reflex/route.py +7 -9
- reflex/state.py +66 -70
- reflex/style.py +3 -1
- reflex/testing.py +46 -29
- reflex/utils/build.py +2 -1
- reflex/utils/console.py +9 -17
- reflex/utils/exec.py +9 -11
- reflex/utils/format.py +21 -24
- reflex/utils/imports.py +4 -3
- reflex/utils/lazy_loader.py +3 -3
- reflex/utils/misc.py +2 -1
- reflex/utils/net.py +2 -2
- reflex/utils/path_ops.py +2 -1
- reflex/utils/prerequisites.py +67 -38
- reflex/utils/processes.py +4 -6
- reflex/utils/pyi_generator.py +46 -41
- reflex/utils/redir.py +1 -1
- reflex/utils/serializers.py +4 -4
- reflex/utils/telemetry.py +42 -4
- reflex/utils/types.py +16 -13
- reflex/vars/base.py +96 -109
- reflex/vars/datetime.py +2 -1
- reflex/vars/dep_tracking.py +19 -28
- reflex/vars/number.py +6 -7
- reflex/vars/object.py +5 -6
- reflex/vars/sequence.py +11 -11
- {reflex-0.7.13a2.dist-info → reflex-0.7.14a1.dist-info}/METADATA +1 -1
- reflex-0.7.14a1.dist-info/RECORD +407 -0
- reflex-0.7.13a2.dist-info/RECORD +0 -407
- {reflex-0.7.13a2.dist-info → reflex-0.7.14a1.dist-info}/WHEEL +0 -0
- {reflex-0.7.13a2.dist-info → reflex-0.7.14a1.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.13a2.dist-info → reflex-0.7.14a1.dist-info}/licenses/LICENSE +0 -0
reflex/utils/prerequisites.py
CHANGED
|
@@ -129,7 +129,6 @@ def check_latest_package_version(package_name: str):
|
|
|
129
129
|
)
|
|
130
130
|
except Exception:
|
|
131
131
|
console.debug(f"Failed to check for the latest version of {package_name}.")
|
|
132
|
-
pass
|
|
133
132
|
|
|
134
133
|
|
|
135
134
|
def get_or_set_last_reflex_version_check_datetime():
|
|
@@ -255,9 +254,8 @@ def get_nodejs_compatible_package_managers(
|
|
|
255
254
|
package_managers = list(filter(None, package_managers))
|
|
256
255
|
|
|
257
256
|
if not package_managers and raise_on_none:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
)
|
|
257
|
+
msg = "Bun or npm not found. You might need to rerun `reflex init` or install either."
|
|
258
|
+
raise FileNotFoundError(msg)
|
|
261
259
|
|
|
262
260
|
return package_managers
|
|
263
261
|
|
|
@@ -310,9 +308,8 @@ def get_js_package_executor(raise_on_none: bool = False) -> Sequence[Sequence[st
|
|
|
310
308
|
package_managers = list(filter(None, package_managers))
|
|
311
309
|
|
|
312
310
|
if not package_managers and raise_on_none:
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
)
|
|
311
|
+
msg = "Bun or npm not found. You might need to rerun `reflex init` or install either."
|
|
312
|
+
raise FileNotFoundError(msg)
|
|
316
313
|
|
|
317
314
|
return package_managers
|
|
318
315
|
|
|
@@ -345,10 +342,11 @@ def _check_app_name(config: Config):
|
|
|
345
342
|
RuntimeError: If the app name is not set in the config.
|
|
346
343
|
"""
|
|
347
344
|
if not config.app_name:
|
|
348
|
-
|
|
345
|
+
msg = (
|
|
349
346
|
"Cannot get the app module because `app_name` is not set in rxconfig! "
|
|
350
347
|
"If this error occurs in a reflex test case, ensure that `get_app` is mocked."
|
|
351
348
|
)
|
|
349
|
+
raise RuntimeError(msg)
|
|
352
350
|
|
|
353
351
|
|
|
354
352
|
def get_app(reload: bool = False) -> ModuleType:
|
|
@@ -395,11 +393,14 @@ def get_app(reload: bool = False) -> ModuleType:
|
|
|
395
393
|
return app
|
|
396
394
|
|
|
397
395
|
|
|
398
|
-
def get_and_validate_app(
|
|
396
|
+
def get_and_validate_app(
|
|
397
|
+
reload: bool = False, check_if_schema_up_to_date: bool = False
|
|
398
|
+
) -> AppInfo:
|
|
399
399
|
"""Get the app instance based on the default config and validate it.
|
|
400
400
|
|
|
401
401
|
Args:
|
|
402
402
|
reload: Re-import the app module from disk
|
|
403
|
+
check_if_schema_up_to_date: If True, check if the schema is up to date.
|
|
403
404
|
|
|
404
405
|
Returns:
|
|
405
406
|
The app instance and the app module.
|
|
@@ -412,23 +413,34 @@ def get_and_validate_app(reload: bool = False) -> AppInfo:
|
|
|
412
413
|
app_module = get_app(reload=reload)
|
|
413
414
|
app = getattr(app_module, constants.CompileVars.APP)
|
|
414
415
|
if not isinstance(app, App):
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
416
|
+
msg = "The app instance in the specified app_module_import in rxconfig must be an instance of rx.App."
|
|
417
|
+
raise RuntimeError(msg)
|
|
418
|
+
|
|
419
|
+
if check_if_schema_up_to_date:
|
|
420
|
+
check_schema_up_to_date()
|
|
421
|
+
|
|
418
422
|
return AppInfo(app=app, module=app_module)
|
|
419
423
|
|
|
420
424
|
|
|
421
|
-
def validate_app(
|
|
425
|
+
def validate_app(
|
|
426
|
+
reload: bool = False, check_if_schema_up_to_date: bool = False
|
|
427
|
+
) -> None:
|
|
422
428
|
"""Validate the app instance based on the default config.
|
|
423
429
|
|
|
424
430
|
Args:
|
|
425
431
|
reload: Re-import the app module from disk
|
|
432
|
+
check_if_schema_up_to_date: If True, check if the schema is up to date.
|
|
426
433
|
"""
|
|
427
|
-
get_and_validate_app(
|
|
434
|
+
get_and_validate_app(
|
|
435
|
+
reload=reload, check_if_schema_up_to_date=check_if_schema_up_to_date
|
|
436
|
+
)
|
|
428
437
|
|
|
429
438
|
|
|
430
439
|
def get_compiled_app(
|
|
431
|
-
reload: bool = False,
|
|
440
|
+
reload: bool = False,
|
|
441
|
+
export: bool = False,
|
|
442
|
+
dry_run: bool = False,
|
|
443
|
+
check_if_schema_up_to_date: bool = False,
|
|
432
444
|
) -> ModuleType:
|
|
433
445
|
"""Get the app module based on the default config after first compiling it.
|
|
434
446
|
|
|
@@ -436,11 +448,14 @@ def get_compiled_app(
|
|
|
436
448
|
reload: Re-import the app module from disk
|
|
437
449
|
export: Compile the app for export
|
|
438
450
|
dry_run: If True, do not write the compiled app to disk.
|
|
451
|
+
check_if_schema_up_to_date: If True, check if the schema is up to date.
|
|
439
452
|
|
|
440
453
|
Returns:
|
|
441
454
|
The compiled app based on the default config.
|
|
442
455
|
"""
|
|
443
|
-
app, app_module = get_and_validate_app(
|
|
456
|
+
app, app_module = get_and_validate_app(
|
|
457
|
+
reload=reload, check_if_schema_up_to_date=check_if_schema_up_to_date
|
|
458
|
+
)
|
|
444
459
|
# For py3.9 compatibility when redis is used, we MUST add any decorator pages
|
|
445
460
|
# before compiling the app in a thread to avoid event loop error (REF-2172).
|
|
446
461
|
app._apply_decorated_pages()
|
|
@@ -449,7 +464,10 @@ def get_compiled_app(
|
|
|
449
464
|
|
|
450
465
|
|
|
451
466
|
def compile_app(
|
|
452
|
-
reload: bool = False,
|
|
467
|
+
reload: bool = False,
|
|
468
|
+
export: bool = False,
|
|
469
|
+
dry_run: bool = False,
|
|
470
|
+
check_if_schema_up_to_date: bool = False,
|
|
453
471
|
) -> None:
|
|
454
472
|
"""Compile the app module based on the default config.
|
|
455
473
|
|
|
@@ -457,8 +475,14 @@ def compile_app(
|
|
|
457
475
|
reload: Re-import the app module from disk
|
|
458
476
|
export: Compile the app for export
|
|
459
477
|
dry_run: If True, do not write the compiled app to disk.
|
|
478
|
+
check_if_schema_up_to_date: If True, check if the schema is up to date.
|
|
460
479
|
"""
|
|
461
|
-
get_compiled_app(
|
|
480
|
+
get_compiled_app(
|
|
481
|
+
reload=reload,
|
|
482
|
+
export=export,
|
|
483
|
+
dry_run=dry_run,
|
|
484
|
+
check_if_schema_up_to_date=check_if_schema_up_to_date,
|
|
485
|
+
)
|
|
462
486
|
|
|
463
487
|
|
|
464
488
|
def _can_colorize() -> bool:
|
|
@@ -503,20 +527,23 @@ def _can_colorize() -> bool:
|
|
|
503
527
|
return file.isatty()
|
|
504
528
|
|
|
505
529
|
|
|
506
|
-
def compile_or_validate_app(
|
|
530
|
+
def compile_or_validate_app(
|
|
531
|
+
compile: bool = False, check_if_schema_up_to_date: bool = False
|
|
532
|
+
) -> bool:
|
|
507
533
|
"""Compile or validate the app module based on the default config.
|
|
508
534
|
|
|
509
535
|
Args:
|
|
510
536
|
compile: Whether to compile the app.
|
|
537
|
+
check_if_schema_up_to_date: If True, check if the schema is up to date.
|
|
511
538
|
|
|
512
539
|
Returns:
|
|
513
540
|
If the app is compiled successfully.
|
|
514
541
|
"""
|
|
515
542
|
try:
|
|
516
543
|
if compile:
|
|
517
|
-
compile_app()
|
|
544
|
+
compile_app(check_if_schema_up_to_date=check_if_schema_up_to_date)
|
|
518
545
|
else:
|
|
519
|
-
validate_app()
|
|
546
|
+
validate_app(check_if_schema_up_to_date=check_if_schema_up_to_date)
|
|
520
547
|
except Exception as e:
|
|
521
548
|
if isinstance(e, click.exceptions.Exit):
|
|
522
549
|
return False
|
|
@@ -575,9 +602,8 @@ def parse_redis_url() -> str | None:
|
|
|
575
602
|
if not config.redis_url:
|
|
576
603
|
return None
|
|
577
604
|
if not config.redis_url.startswith(("redis://", "rediss://", "unix://")):
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
)
|
|
605
|
+
msg = "REDIS_URL must start with 'redis://', 'rediss://', or 'unix://'."
|
|
606
|
+
raise ValueError(msg)
|
|
581
607
|
return config.redis_url
|
|
582
608
|
|
|
583
609
|
|
|
@@ -1157,15 +1183,16 @@ def download_and_run(url: str, *args, show_status: bool = False, **env):
|
|
|
1157
1183
|
raise click.exceptions.Exit(1) from None
|
|
1158
1184
|
|
|
1159
1185
|
# Save the script to a temporary file.
|
|
1160
|
-
|
|
1186
|
+
with tempfile.NamedTemporaryFile() as tempfile_file:
|
|
1187
|
+
script = Path(tempfile_file.name)
|
|
1161
1188
|
|
|
1162
|
-
|
|
1189
|
+
script.write_text(response.text)
|
|
1163
1190
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1191
|
+
# Run the script.
|
|
1192
|
+
env = {**os.environ, **env}
|
|
1193
|
+
process = processes.new_process(["bash", str(script), *args], env=env)
|
|
1194
|
+
show = processes.show_status if show_status else processes.show_logs
|
|
1195
|
+
show(f"Installing {url}", process)
|
|
1169
1196
|
|
|
1170
1197
|
|
|
1171
1198
|
def install_bun():
|
|
@@ -1213,7 +1240,8 @@ def install_bun():
|
|
|
1213
1240
|
)
|
|
1214
1241
|
else:
|
|
1215
1242
|
if path_ops.which("unzip") is None:
|
|
1216
|
-
|
|
1243
|
+
msg = "unzip"
|
|
1244
|
+
raise SystemPackageMissingError(msg)
|
|
1217
1245
|
|
|
1218
1246
|
# Run the bun install script.
|
|
1219
1247
|
download_and_run(
|
|
@@ -1262,13 +1290,15 @@ def cached_procedure(
|
|
|
1262
1290
|
ValueError: If both cache_file and cache_file_fn are provided.
|
|
1263
1291
|
"""
|
|
1264
1292
|
if cache_file and cache_file_fn is not None:
|
|
1265
|
-
|
|
1293
|
+
msg = "cache_file and cache_file_fn cannot both be provided."
|
|
1294
|
+
raise ValueError(msg)
|
|
1266
1295
|
|
|
1267
1296
|
def _inner_decorator(func: Callable):
|
|
1268
1297
|
def _inner(*args, **kwargs):
|
|
1269
1298
|
_cache_file = cache_file_fn() if cache_file_fn is not None else cache_file
|
|
1270
1299
|
if not _cache_file:
|
|
1271
|
-
|
|
1300
|
+
msg = "Unknown cache file, cannot cache result."
|
|
1301
|
+
raise ValueError(msg)
|
|
1272
1302
|
payload = _read_cached_procedure_file(_cache_file)
|
|
1273
1303
|
new_payload = payload_fn(*args, **kwargs)
|
|
1274
1304
|
if payload != new_payload:
|
|
@@ -1446,7 +1476,7 @@ def validate_bun(bun_path: Path | None = None):
|
|
|
1446
1476
|
"Failed to obtain bun version. Make sure the specified bun path in your config is correct."
|
|
1447
1477
|
)
|
|
1448
1478
|
raise click.exceptions.Exit(1)
|
|
1449
|
-
|
|
1479
|
+
if bun_version < version.parse(constants.Bun.MIN_VERSION):
|
|
1450
1480
|
console.warn(
|
|
1451
1481
|
f"Reflex requires bun version {constants.Bun.MIN_VERSION} or higher to run, but the detected version is "
|
|
1452
1482
|
f"{bun_version}. If you have specified a custom bun path in your config, make sure to provide one "
|
|
@@ -1657,8 +1687,7 @@ def fetch_app_templates(version: str) -> dict[str, Template]:
|
|
|
1657
1687
|
if asset is None:
|
|
1658
1688
|
console.warn(f"Templates metadata not found for version {version}")
|
|
1659
1689
|
return {}
|
|
1660
|
-
|
|
1661
|
-
templates_url = asset["browser_download_url"]
|
|
1690
|
+
templates_url = asset["browser_download_url"]
|
|
1662
1691
|
|
|
1663
1692
|
templates_data = net.get(templates_url, follow_redirects=True).json()["templates"]
|
|
1664
1693
|
|
|
@@ -1864,7 +1893,7 @@ def initialize_app(app_name: str, template: str | None = None) -> str | None:
|
|
|
1864
1893
|
# Check if the app is already initialized.
|
|
1865
1894
|
if constants.Config.FILE.exists():
|
|
1866
1895
|
telemetry.send("reinit")
|
|
1867
|
-
return
|
|
1896
|
+
return None
|
|
1868
1897
|
|
|
1869
1898
|
templates: dict[str, Template] = {}
|
|
1870
1899
|
|
reflex/utils/processes.py
CHANGED
|
@@ -137,11 +137,10 @@ def handle_port(service_name: str, port: int, auto_increment: bool) -> int:
|
|
|
137
137
|
return port
|
|
138
138
|
if auto_increment:
|
|
139
139
|
return change_port(port, service_name)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
raise click.exceptions.Exit()
|
|
140
|
+
console.error(
|
|
141
|
+
f"{service_name.capitalize()} port: {port} is already in use by PID: {process.pid}."
|
|
142
|
+
)
|
|
143
|
+
raise click.exceptions.Exit
|
|
145
144
|
|
|
146
145
|
|
|
147
146
|
@overload
|
|
@@ -316,7 +315,6 @@ def stream_logs(
|
|
|
316
315
|
# But if the process is still running that is weird.
|
|
317
316
|
raise
|
|
318
317
|
# If the process exited, break out of the loop for post processing.
|
|
319
|
-
pass
|
|
320
318
|
|
|
321
319
|
# Check if the process failed (not printing the logs for SIGINT).
|
|
322
320
|
|
reflex/utils/pyi_generator.py
CHANGED
|
@@ -30,6 +30,8 @@ logger = logging.getLogger("pyi_generator")
|
|
|
30
30
|
|
|
31
31
|
PWD = Path.cwd()
|
|
32
32
|
|
|
33
|
+
PYI_HASHES = "pyi_hashes.json"
|
|
34
|
+
|
|
33
35
|
EXCLUDED_FILES = [
|
|
34
36
|
"app.py",
|
|
35
37
|
"component.py",
|
|
@@ -157,9 +159,8 @@ def _get_type_hint(
|
|
|
157
159
|
res_args.sort()
|
|
158
160
|
if len(res_args) == 1:
|
|
159
161
|
return f"{res_args[0]} | None"
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return f"{res} | None"
|
|
162
|
+
res = f"{' | '.join(res_args)}"
|
|
163
|
+
return f"{res} | None"
|
|
163
164
|
|
|
164
165
|
res_args = [
|
|
165
166
|
_get_type_hint(arg, type_hint_globals, rx_types.is_optional(arg))
|
|
@@ -183,10 +184,11 @@ def _get_type_hint(
|
|
|
183
184
|
value.__module__ not in ["builtins", "__builtins__"]
|
|
184
185
|
and value.__name__ not in type_hint_globals
|
|
185
186
|
):
|
|
186
|
-
|
|
187
|
+
msg = (
|
|
187
188
|
f"{value.__module__ + '.' + value.__name__} is not a default import, "
|
|
188
189
|
"add it to DEFAULT_IMPORTS in pyi_generator.py"
|
|
189
190
|
)
|
|
191
|
+
raise TypeError(msg)
|
|
190
192
|
|
|
191
193
|
res = f"{value.__name__}[{', '.join(inner_container_type_args)}]"
|
|
192
194
|
|
|
@@ -445,7 +447,7 @@ def type_to_ast(typ: Any, cls: type) -> ast.expr:
|
|
|
445
447
|
|
|
446
448
|
return ast.Name(id=typ.__module__ + "." + typ.__name__)
|
|
447
449
|
return ast.Name(id=typ.__name__)
|
|
448
|
-
|
|
450
|
+
if hasattr(typ, "_name"):
|
|
449
451
|
return ast.Name(id=typ._name)
|
|
450
452
|
return ast.Name(id=str(typ))
|
|
451
453
|
|
|
@@ -510,7 +512,8 @@ def _generate_component_create_functiondef(
|
|
|
510
512
|
TypeError: If clz is not a subclass of Component.
|
|
511
513
|
"""
|
|
512
514
|
if not issubclass(clz, Component):
|
|
513
|
-
|
|
515
|
+
msg = f"clz must be a subclass of Component, not {clz!r}"
|
|
516
|
+
raise TypeError(msg)
|
|
514
517
|
|
|
515
518
|
# add the imports needed by get_type_hint later
|
|
516
519
|
type_hint_globals.update(
|
|
@@ -654,7 +657,7 @@ def _generate_component_create_functiondef(
|
|
|
654
657
|
defaults=[],
|
|
655
658
|
)
|
|
656
659
|
|
|
657
|
-
|
|
660
|
+
return ast.FunctionDef( # pyright: ignore [reportCallIssue]
|
|
658
661
|
name="create",
|
|
659
662
|
args=create_args,
|
|
660
663
|
body=[
|
|
@@ -676,7 +679,6 @@ def _generate_component_create_functiondef(
|
|
|
676
679
|
lineno=lineno,
|
|
677
680
|
returns=ast.Constant(value=clz.__name__),
|
|
678
681
|
)
|
|
679
|
-
return definition
|
|
680
682
|
|
|
681
683
|
|
|
682
684
|
def _generate_staticmethod_call_functiondef(
|
|
@@ -710,7 +712,7 @@ def _generate_staticmethod_call_functiondef(
|
|
|
710
712
|
else []
|
|
711
713
|
),
|
|
712
714
|
)
|
|
713
|
-
|
|
715
|
+
return ast.FunctionDef( # pyright: ignore [reportCallIssue]
|
|
714
716
|
name="__call__",
|
|
715
717
|
args=call_args,
|
|
716
718
|
body=[
|
|
@@ -729,7 +731,6 @@ def _generate_staticmethod_call_functiondef(
|
|
|
729
731
|
)
|
|
730
732
|
),
|
|
731
733
|
)
|
|
732
|
-
return definition
|
|
733
734
|
|
|
734
735
|
|
|
735
736
|
def _generate_namespace_call_functiondef(
|
|
@@ -841,6 +842,7 @@ class StubGenerator(ast.NodeTransformer):
|
|
|
841
842
|
and issubclass((clz := self.classes[self.current_class]), Component)
|
|
842
843
|
):
|
|
843
844
|
return clz
|
|
845
|
+
return None
|
|
844
846
|
|
|
845
847
|
def visit_Module(self, node: ast.Module) -> ast.Module:
|
|
846
848
|
"""Visit a Module node and remove docstring from body.
|
|
@@ -1021,7 +1023,7 @@ class StubGenerator(ast.NodeTransformer):
|
|
|
1021
1023
|
if isinstance(target, ast.Tuple):
|
|
1022
1024
|
for name in target.elts:
|
|
1023
1025
|
if isinstance(name, ast.Name) and name.id.startswith("_"):
|
|
1024
|
-
return
|
|
1026
|
+
return None
|
|
1025
1027
|
|
|
1026
1028
|
return node
|
|
1027
1029
|
|
|
@@ -1107,7 +1109,7 @@ class PyiGenerator:
|
|
|
1107
1109
|
pyright_ignore_imports = getattr(mod, "_PYRIGHT_IGNORE_IMPORTS", [])
|
|
1108
1110
|
|
|
1109
1111
|
if not sub_mods and not sub_mod_attrs:
|
|
1110
|
-
return
|
|
1112
|
+
return None
|
|
1111
1113
|
sub_mods_imports = []
|
|
1112
1114
|
sub_mod_attrs_imports = []
|
|
1113
1115
|
|
|
@@ -1162,7 +1164,7 @@ class PyiGenerator:
|
|
|
1162
1164
|
}
|
|
1163
1165
|
is_init_file = _relative_to_pwd(module_path).name == "__init__.py"
|
|
1164
1166
|
if not class_names and not is_init_file:
|
|
1165
|
-
return
|
|
1167
|
+
return None
|
|
1166
1168
|
|
|
1167
1169
|
if is_init_file:
|
|
1168
1170
|
new_tree = InitStubGenerator(module, class_names).visit(
|
|
@@ -1170,7 +1172,7 @@ class PyiGenerator:
|
|
|
1170
1172
|
)
|
|
1171
1173
|
init_imports = self._get_init_lazy_imports(module, new_tree)
|
|
1172
1174
|
if not init_imports:
|
|
1173
|
-
return
|
|
1175
|
+
return None
|
|
1174
1176
|
content_hash = self._write_pyi_file(module_path, init_imports)
|
|
1175
1177
|
else:
|
|
1176
1178
|
new_tree = StubGenerator(module, class_names).visit(
|
|
@@ -1264,45 +1266,48 @@ class PyiGenerator:
|
|
|
1264
1266
|
file_parent = file_path.parent
|
|
1265
1267
|
while len(file_parent.parts) > len(top_dir.parts):
|
|
1266
1268
|
file_parent = file_parent.parent
|
|
1269
|
+
while len(top_dir.parts) > len(file_parent.parts):
|
|
1270
|
+
top_dir = top_dir.parent
|
|
1267
1271
|
while not file_parent.samefile(top_dir):
|
|
1268
1272
|
file_parent = file_parent.parent
|
|
1269
1273
|
top_dir = top_dir.parent
|
|
1270
1274
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
pyi_hashes_file.
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1275
|
+
while (
|
|
1276
|
+
not top_dir.samefile(top_dir.parent)
|
|
1277
|
+
and not (top_dir / PYI_HASHES).exists()
|
|
1278
|
+
):
|
|
1279
|
+
top_dir = top_dir.parent
|
|
1280
|
+
|
|
1281
|
+
pyi_hashes_file = top_dir / PYI_HASHES
|
|
1282
|
+
|
|
1283
|
+
if pyi_hashes_file.exists():
|
|
1284
|
+
pyi_hashes_file.write_text(
|
|
1285
|
+
json.dumps(
|
|
1286
|
+
dict(
|
|
1287
|
+
zip(
|
|
1288
|
+
[
|
|
1289
|
+
f.relative_to(pyi_hashes_file.parent).as_posix()
|
|
1290
|
+
for f in file_paths
|
|
1291
|
+
],
|
|
1292
|
+
hashes,
|
|
1293
|
+
strict=True,
|
|
1294
|
+
)
|
|
1295
|
+
),
|
|
1296
|
+
indent=2,
|
|
1297
|
+
sort_keys=True,
|
|
1298
|
+
)
|
|
1299
|
+
+ "\n",
|
|
1293
1300
|
)
|
|
1294
|
-
+ "\n",
|
|
1295
|
-
)
|
|
1296
1301
|
elif file_paths:
|
|
1297
1302
|
file_paths = list(map(Path, file_paths))
|
|
1298
1303
|
pyi_hashes_parent = file_paths[0].parent
|
|
1299
1304
|
while (
|
|
1300
|
-
pyi_hashes_parent.parent
|
|
1301
|
-
and not (pyi_hashes_parent /
|
|
1305
|
+
not pyi_hashes_parent.samefile(pyi_hashes_parent.parent)
|
|
1306
|
+
and not (pyi_hashes_parent / PYI_HASHES).exists()
|
|
1302
1307
|
):
|
|
1303
1308
|
pyi_hashes_parent = pyi_hashes_parent.parent
|
|
1304
1309
|
|
|
1305
|
-
pyi_hashes_file = pyi_hashes_parent /
|
|
1310
|
+
pyi_hashes_file = pyi_hashes_parent / PYI_HASHES
|
|
1306
1311
|
if pyi_hashes_file.exists():
|
|
1307
1312
|
pyi_hashes = json.loads(pyi_hashes_file.read_text())
|
|
1308
1313
|
for file_path, hashed_content in zip(
|
reflex/utils/redir.py
CHANGED
reflex/utils/serializers.py
CHANGED
|
@@ -76,7 +76,8 @@ def serializer(
|
|
|
76
76
|
|
|
77
77
|
# Make sure the function takes a single argument.
|
|
78
78
|
if len(args) != 1:
|
|
79
|
-
|
|
79
|
+
msg = "Serializer must take a single argument."
|
|
80
|
+
raise ValueError(msg)
|
|
80
81
|
|
|
81
82
|
# Get the type of the argument.
|
|
82
83
|
type_ = type_hints[args[0]]
|
|
@@ -166,8 +167,7 @@ def serialize(
|
|
|
166
167
|
# Return the serialized value and the type.
|
|
167
168
|
if get_type:
|
|
168
169
|
return serialized, get_serializer_type(type(value))
|
|
169
|
-
|
|
170
|
-
return serialized
|
|
170
|
+
return serialized
|
|
171
171
|
|
|
172
172
|
|
|
173
173
|
@functools.lru_cache
|
|
@@ -427,7 +427,7 @@ with contextlib.suppress(ImportError):
|
|
|
427
427
|
"""
|
|
428
428
|
return [
|
|
429
429
|
[str(d) if isinstance(d, (list, tuple)) else d for d in data]
|
|
430
|
-
for data in list(df.
|
|
430
|
+
for data in list(df.to_numpy().tolist())
|
|
431
431
|
]
|
|
432
432
|
|
|
433
433
|
@serializer
|
reflex/utils/telemetry.py
CHANGED
|
@@ -19,7 +19,12 @@ from reflex.config import environment
|
|
|
19
19
|
from reflex.utils import console
|
|
20
20
|
from reflex.utils.decorator import once_unless_none
|
|
21
21
|
from reflex.utils.exceptions import ReflexError
|
|
22
|
-
from reflex.utils.prerequisites import
|
|
22
|
+
from reflex.utils.prerequisites import (
|
|
23
|
+
ensure_reflex_installation_id,
|
|
24
|
+
get_bun_version,
|
|
25
|
+
get_node_version,
|
|
26
|
+
get_project_hash,
|
|
27
|
+
)
|
|
23
28
|
|
|
24
29
|
UTC = timezone.utc
|
|
25
30
|
POSTHOG_API_URL: str = "https://app.posthog.com/capture/"
|
|
@@ -62,6 +67,22 @@ def get_reflex_version() -> str:
|
|
|
62
67
|
return constants.Reflex.VERSION
|
|
63
68
|
|
|
64
69
|
|
|
70
|
+
def _get_app_class_name() -> str | None:
|
|
71
|
+
"""Get the app class name if available.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
The app class name (e.g. "App", "AppEnterprise") or None if not available.
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
from reflex.utils.prerequisites import get_and_validate_app
|
|
78
|
+
|
|
79
|
+
app_info = get_and_validate_app()
|
|
80
|
+
except Exception:
|
|
81
|
+
return None
|
|
82
|
+
else:
|
|
83
|
+
return app_info.app.__class__.__name__
|
|
84
|
+
|
|
85
|
+
|
|
65
86
|
def get_cpu_count() -> int:
|
|
66
87
|
"""Get the number of CPUs.
|
|
67
88
|
|
|
@@ -106,6 +127,8 @@ class _Properties(TypedDict):
|
|
|
106
127
|
user_os_detail: str
|
|
107
128
|
reflex_version: str
|
|
108
129
|
python_version: str
|
|
130
|
+
node_version: str | None
|
|
131
|
+
bun_version: str | None
|
|
109
132
|
cpu_count: int
|
|
110
133
|
memory: int
|
|
111
134
|
cpu_info: dict
|
|
@@ -153,6 +176,12 @@ def _get_event_defaults() -> _DefaultEvent | None:
|
|
|
153
176
|
"user_os_detail": get_detailed_platform_str(),
|
|
154
177
|
"reflex_version": get_reflex_version(),
|
|
155
178
|
"python_version": get_python_version(),
|
|
179
|
+
"node_version": (
|
|
180
|
+
str(node_version) if (node_version := get_node_version()) else None
|
|
181
|
+
),
|
|
182
|
+
"bun_version": (
|
|
183
|
+
str(bun_version) if (bun_version := get_bun_version()) else None
|
|
184
|
+
),
|
|
156
185
|
"cpu_count": get_cpu_count(),
|
|
157
186
|
"memory": get_memory(),
|
|
158
187
|
"cpu_info": dataclasses.asdict(cpuinfo) if cpuinfo else {},
|
|
@@ -184,12 +213,16 @@ def _prepare_event(event: str, **kwargs) -> _Event | None:
|
|
|
184
213
|
if not event_data:
|
|
185
214
|
return None
|
|
186
215
|
|
|
187
|
-
additional_keys = ["template", "context", "detail", "user_uuid"]
|
|
216
|
+
additional_keys = ["template", "context", "detail", "user_uuid", "app_class"]
|
|
188
217
|
|
|
189
218
|
properties = event_data["properties"]
|
|
190
219
|
|
|
220
|
+
# Auto-detect app class if not provided
|
|
221
|
+
if "app_class" not in kwargs:
|
|
222
|
+
kwargs["app_class"] = _get_app_class_name()
|
|
223
|
+
|
|
191
224
|
for key in additional_keys:
|
|
192
|
-
if key in properties or key not in kwargs:
|
|
225
|
+
if key in properties or key not in kwargs or kwargs[key] is None:
|
|
193
226
|
continue
|
|
194
227
|
|
|
195
228
|
properties[key] = kwargs[key]
|
|
@@ -232,6 +265,9 @@ def _send(event: str, telemetry_enabled: bool | None, **kwargs) -> bool:
|
|
|
232
265
|
return False
|
|
233
266
|
|
|
234
267
|
|
|
268
|
+
background_tasks = set()
|
|
269
|
+
|
|
270
|
+
|
|
235
271
|
def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
|
|
236
272
|
"""Send anonymous telemetry for Reflex.
|
|
237
273
|
|
|
@@ -246,7 +282,9 @@ def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
|
|
|
246
282
|
|
|
247
283
|
try:
|
|
248
284
|
# Within an event loop context, send the event asynchronously.
|
|
249
|
-
asyncio.create_task(async_send(event, telemetry_enabled, **kwargs))
|
|
285
|
+
task = asyncio.create_task(async_send(event, telemetry_enabled, **kwargs))
|
|
286
|
+
background_tasks.add(task)
|
|
287
|
+
task.add_done_callback(background_tasks.discard)
|
|
250
288
|
except RuntimeError:
|
|
251
289
|
# If there is no event loop, send the event synchronously.
|
|
252
290
|
warnings.filterwarnings("ignore", category=RuntimeWarning)
|