reflex 0.7.13a1__py3-none-any.whl → 0.7.14__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 +100 -92
- reflex/app_mixins/lifespan.py +2 -3
- reflex/app_mixins/middleware.py +1 -0
- reflex/app_mixins/mixin.py +0 -1
- reflex/assets.py +7 -4
- reflex/base.py +3 -2
- reflex/compiler/compiler.py +79 -65
- reflex/compiler/utils.py +8 -6
- reflex/components/base/app_wrap.pyi +0 -1
- reflex/components/base/bare.py +22 -12
- 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 +85 -45
- reflex/components/core/auto_scroll.pyi +0 -1
- reflex/components/core/banner.py +1 -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.py +1 -1
- 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 +14 -15
- reflex/components/el/elements/forms.pyi +15 -32
- 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.py +1 -1
- 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.py +1 -1
- reflex/components/radix/primitives/drawer.pyi +0 -11
- reflex/components/radix/primitives/form.py +5 -9
- 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 +2 -4
- 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 +41 -615
- reflex/constants/base.py +6 -6
- reflex/constants/compiler.py +8 -6
- reflex/constants/installer.py +25 -16
- reflex/custom_components/custom_components.py +1 -2
- reflex/environment.py +606 -0
- 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 +17 -20
- reflex/istate/proxy.py +19 -12
- reflex/model.py +8 -5
- reflex/plugins/base.py +8 -0
- reflex/plugins/tailwind_v3.py +8 -0
- reflex/plugins/tailwind_v4.py +9 -0
- reflex/reflex.py +11 -12
- reflex/route.py +7 -9
- reflex/state.py +67 -71
- reflex/style.py +3 -1
- reflex/testing.py +49 -30
- reflex/utils/build.py +2 -1
- reflex/utils/console.py +70 -17
- reflex/utils/exec.py +113 -39
- reflex/utils/export.py +2 -1
- 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 +4 -2
- reflex/utils/prerequisites.py +69 -39
- reflex/utils/processes.py +5 -7
- reflex/utils/pyi_generator.py +46 -41
- reflex/utils/redir.py +1 -1
- reflex/utils/registry.py +1 -1
- reflex/utils/serializers.py +4 -4
- reflex/utils/telemetry.py +36 -3
- 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.13a1.dist-info → reflex-0.7.14.dist-info}/METADATA +1 -1
- reflex-0.7.14.dist-info/RECORD +408 -0
- reflex-0.7.13a1.dist-info/RECORD +0 -407
- {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/WHEEL +0 -0
- {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/licenses/LICENSE +0 -0
reflex/testing.py
CHANGED
|
@@ -28,6 +28,7 @@ import psutil
|
|
|
28
28
|
import uvicorn
|
|
29
29
|
|
|
30
30
|
import reflex
|
|
31
|
+
import reflex.environment
|
|
31
32
|
import reflex.reflex
|
|
32
33
|
import reflex.utils.build
|
|
33
34
|
import reflex.utils.exec
|
|
@@ -35,7 +36,8 @@ import reflex.utils.format
|
|
|
35
36
|
import reflex.utils.prerequisites
|
|
36
37
|
import reflex.utils.processes
|
|
37
38
|
from reflex.components.component import CustomComponent
|
|
38
|
-
from reflex.config import
|
|
39
|
+
from reflex.config import get_config
|
|
40
|
+
from reflex.environment import environment
|
|
39
41
|
from reflex.state import (
|
|
40
42
|
BaseState,
|
|
41
43
|
StateManager,
|
|
@@ -155,9 +157,8 @@ class AppHarness:
|
|
|
155
157
|
app_name = f"{func_name}_{slug_suffix}"
|
|
156
158
|
app_name = re.sub(r"[^a-zA-Z0-9_]", "_", app_name)
|
|
157
159
|
elif isinstance(app_source, str):
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
160
|
+
msg = "app_name must be provided when app_source is a string."
|
|
161
|
+
raise ValueError(msg)
|
|
161
162
|
else:
|
|
162
163
|
app_name = app_source.__name__
|
|
163
164
|
|
|
@@ -285,7 +286,8 @@ class AppHarness:
|
|
|
285
286
|
self.app_instance._state_manager, StateManagerRedis
|
|
286
287
|
):
|
|
287
288
|
if self.app_instance._state is None:
|
|
288
|
-
|
|
289
|
+
msg = "State is not set."
|
|
290
|
+
raise RuntimeError(msg)
|
|
289
291
|
# Create our own redis connection for testing.
|
|
290
292
|
self.state_manager = StateManagerRedis.create(self.app_instance._state)
|
|
291
293
|
else:
|
|
@@ -299,7 +301,8 @@ class AppHarness:
|
|
|
299
301
|
|
|
300
302
|
def _get_backend_shutdown_handler(self):
|
|
301
303
|
if self.backend is None:
|
|
302
|
-
|
|
304
|
+
msg = "Backend was not initialized."
|
|
305
|
+
raise RuntimeError(msg)
|
|
303
306
|
|
|
304
307
|
original_shutdown = self.backend.shutdown
|
|
305
308
|
|
|
@@ -330,7 +333,8 @@ class AppHarness:
|
|
|
330
333
|
|
|
331
334
|
def _start_backend(self, port: int = 0):
|
|
332
335
|
if self.app_asgi is None:
|
|
333
|
-
|
|
336
|
+
msg = "App was not initialized."
|
|
337
|
+
raise RuntimeError(msg)
|
|
334
338
|
self.backend = uvicorn.Server(
|
|
335
339
|
uvicorn.Config(
|
|
336
340
|
app=self.app_asgi,
|
|
@@ -366,7 +370,8 @@ class AppHarness:
|
|
|
366
370
|
state=self.app_instance._state,
|
|
367
371
|
)
|
|
368
372
|
if not isinstance(self.app_instance.state_manager, StateManagerRedis):
|
|
369
|
-
|
|
373
|
+
msg = "Failed to reset state manager."
|
|
374
|
+
raise RuntimeError(msg)
|
|
370
375
|
|
|
371
376
|
def _start_frontend(self):
|
|
372
377
|
# Set up the frontend.
|
|
@@ -406,7 +411,8 @@ class AppHarness:
|
|
|
406
411
|
config.deploy_url = self.frontend_url
|
|
407
412
|
break
|
|
408
413
|
if self.frontend_url is None:
|
|
409
|
-
|
|
414
|
+
msg = "Frontend did not start"
|
|
415
|
+
raise RuntimeError(msg)
|
|
410
416
|
|
|
411
417
|
def consume_frontend_output():
|
|
412
418
|
while True:
|
|
@@ -578,20 +584,23 @@ class AppHarness:
|
|
|
578
584
|
TimeoutError: when server or sockets are not ready
|
|
579
585
|
"""
|
|
580
586
|
if self.backend is None:
|
|
581
|
-
|
|
587
|
+
msg = "Backend is not running."
|
|
588
|
+
raise RuntimeError(msg)
|
|
582
589
|
backend = self.backend
|
|
583
590
|
# check for servers to be initialized
|
|
584
591
|
if not self._poll_for(
|
|
585
592
|
target=lambda: getattr(backend, "servers", False),
|
|
586
593
|
timeout=timeout,
|
|
587
594
|
):
|
|
588
|
-
|
|
595
|
+
msg = "Backend servers are not initialized."
|
|
596
|
+
raise TimeoutError(msg)
|
|
589
597
|
# check for sockets to be listening
|
|
590
598
|
if not self._poll_for(
|
|
591
599
|
target=lambda: getattr(backend.servers[0], "sockets", False),
|
|
592
600
|
timeout=timeout,
|
|
593
601
|
):
|
|
594
|
-
|
|
602
|
+
msg = "Backend is not listening."
|
|
603
|
+
raise TimeoutError(msg)
|
|
595
604
|
return backend.servers[0].sockets[0]
|
|
596
605
|
|
|
597
606
|
def frontend(
|
|
@@ -619,12 +628,14 @@ class AppHarness:
|
|
|
619
628
|
RuntimeError: when selenium is not importable or frontend is not running
|
|
620
629
|
"""
|
|
621
630
|
if not has_selenium:
|
|
622
|
-
|
|
631
|
+
msg = (
|
|
623
632
|
"Frontend functionality requires `selenium` to be installed, "
|
|
624
633
|
"and it could not be imported."
|
|
625
634
|
)
|
|
635
|
+
raise RuntimeError(msg)
|
|
626
636
|
if self.frontend_url is None:
|
|
627
|
-
|
|
637
|
+
msg = "Frontend is not running."
|
|
638
|
+
raise RuntimeError(msg)
|
|
628
639
|
want_headless = False
|
|
629
640
|
if environment.APP_HARNESS_HEADLESS.get():
|
|
630
641
|
want_headless = True
|
|
@@ -650,7 +661,8 @@ class AppHarness:
|
|
|
650
661
|
if want_headless:
|
|
651
662
|
driver_options.add_argument("headless")
|
|
652
663
|
if driver_options is None:
|
|
653
|
-
|
|
664
|
+
msg = f"Could not determine options for {driver_clz}"
|
|
665
|
+
raise RuntimeError(msg)
|
|
654
666
|
if args := environment.APP_HARNESS_DRIVER_ARGS.get():
|
|
655
667
|
for arg in args.split(","):
|
|
656
668
|
driver_options.add_argument(arg)
|
|
@@ -680,7 +692,8 @@ class AppHarness:
|
|
|
680
692
|
RuntimeError: when the app hasn't started running
|
|
681
693
|
"""
|
|
682
694
|
if self.state_manager is None:
|
|
683
|
-
|
|
695
|
+
msg = "state_manager is not set."
|
|
696
|
+
raise RuntimeError(msg)
|
|
684
697
|
try:
|
|
685
698
|
return await self.state_manager.get_state(token)
|
|
686
699
|
finally:
|
|
@@ -698,7 +711,8 @@ class AppHarness:
|
|
|
698
711
|
RuntimeError: when the app hasn't started running
|
|
699
712
|
"""
|
|
700
713
|
if self.state_manager is None:
|
|
701
|
-
|
|
714
|
+
msg = "state_manager is not set."
|
|
715
|
+
raise RuntimeError(msg)
|
|
702
716
|
state = await self.get_state(token)
|
|
703
717
|
for key, value in kwargs.items():
|
|
704
718
|
setattr(state, key, value)
|
|
@@ -722,9 +736,11 @@ class AppHarness:
|
|
|
722
736
|
RuntimeError: when the app hasn't started running
|
|
723
737
|
"""
|
|
724
738
|
if self.state_manager is None:
|
|
725
|
-
|
|
739
|
+
msg = "state_manager is not set."
|
|
740
|
+
raise RuntimeError(msg)
|
|
726
741
|
if self.app_instance is None:
|
|
727
|
-
|
|
742
|
+
msg = "App is not running."
|
|
743
|
+
raise RuntimeError(msg)
|
|
728
744
|
app_state_manager = self.app_instance.state_manager
|
|
729
745
|
if isinstance(self.state_manager, StateManagerRedis):
|
|
730
746
|
# Temporarily replace the app's state manager with our own, since
|
|
@@ -761,9 +777,8 @@ class AppHarness:
|
|
|
761
777
|
target=lambda: element.text != exp_not_equal,
|
|
762
778
|
timeout=timeout,
|
|
763
779
|
):
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
)
|
|
780
|
+
msg = f"{element} content remains {exp_not_equal!r} while polling."
|
|
781
|
+
raise TimeoutError(msg)
|
|
767
782
|
return element.text
|
|
768
783
|
|
|
769
784
|
def poll_for_value(
|
|
@@ -792,9 +807,8 @@ class AppHarness:
|
|
|
792
807
|
target=lambda: element.get_attribute("value") not in exp_not_equal,
|
|
793
808
|
timeout=timeout,
|
|
794
809
|
):
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
)
|
|
810
|
+
msg = f"{element} content remains {exp_not_equal!r} while polling."
|
|
811
|
+
raise TimeoutError(msg)
|
|
798
812
|
return element.get_attribute("value")
|
|
799
813
|
|
|
800
814
|
def poll_for_clients(self, timeout: TimeoutType = None) -> dict[str, BaseState]:
|
|
@@ -812,15 +826,18 @@ class AppHarness:
|
|
|
812
826
|
ValueError: when the state_manager is not a memory state manager
|
|
813
827
|
"""
|
|
814
828
|
if self.app_instance is None:
|
|
815
|
-
|
|
829
|
+
msg = "App is not running."
|
|
830
|
+
raise RuntimeError(msg)
|
|
816
831
|
state_manager = self.app_instance.state_manager
|
|
817
832
|
if not isinstance(state_manager, (StateManagerMemory, StateManagerDisk)):
|
|
818
|
-
|
|
833
|
+
msg = "Only works with memory or disk state manager"
|
|
834
|
+
raise ValueError(msg)
|
|
819
835
|
if not self._poll_for(
|
|
820
836
|
target=lambda: state_manager.states,
|
|
821
837
|
timeout=timeout,
|
|
822
838
|
):
|
|
823
|
-
|
|
839
|
+
msg = "No states were observed while polling."
|
|
840
|
+
raise TimeoutError(msg)
|
|
824
841
|
return state_manager.states
|
|
825
842
|
|
|
826
843
|
|
|
@@ -962,11 +979,13 @@ class AppHarnessProd(AppHarness):
|
|
|
962
979
|
def _wait_frontend(self):
|
|
963
980
|
self._poll_for(lambda: self.frontend_server is not None)
|
|
964
981
|
if self.frontend_server is None or not self.frontend_server.socket.fileno():
|
|
965
|
-
|
|
982
|
+
msg = "Frontend did not start"
|
|
983
|
+
raise RuntimeError(msg)
|
|
966
984
|
|
|
967
985
|
def _start_backend(self):
|
|
968
986
|
if self.app_asgi is None:
|
|
969
|
-
|
|
987
|
+
msg = "App was not initialized."
|
|
988
|
+
raise RuntimeError(msg)
|
|
970
989
|
environment.REFLEX_SKIP_COMPILE.set(True)
|
|
971
990
|
self.backend = uvicorn.Server(
|
|
972
991
|
uvicorn.Config(
|
reflex/utils/build.py
CHANGED
|
@@ -132,7 +132,7 @@ def _zip(
|
|
|
132
132
|
def zip_app(
|
|
133
133
|
frontend: bool = True,
|
|
134
134
|
backend: bool = True,
|
|
135
|
-
zip_dest_dir: str | Path =
|
|
135
|
+
zip_dest_dir: str | Path | None = None,
|
|
136
136
|
upload_db_file: bool = False,
|
|
137
137
|
):
|
|
138
138
|
"""Zip up the app.
|
|
@@ -143,6 +143,7 @@ def zip_app(
|
|
|
143
143
|
zip_dest_dir: The directory to export the zip file to.
|
|
144
144
|
upload_db_file: Whether to upload the database file.
|
|
145
145
|
"""
|
|
146
|
+
zip_dest_dir = zip_dest_dir or Path.cwd()
|
|
146
147
|
zip_dest_dir = Path(zip_dest_dir)
|
|
147
148
|
files_to_exclude = {
|
|
148
149
|
constants.ComponentName.FRONTEND.zip(),
|
reflex/utils/console.py
CHANGED
|
@@ -15,6 +15,8 @@ from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
|
|
15
15
|
from rich.prompt import Prompt
|
|
16
16
|
|
|
17
17
|
from reflex.constants import LogLevel
|
|
18
|
+
from reflex.constants.base import Reflex
|
|
19
|
+
from reflex.utils.decorator import once
|
|
18
20
|
|
|
19
21
|
# Console for pretty printing.
|
|
20
22
|
_console = Console()
|
|
@@ -59,9 +61,8 @@ def set_log_level(log_level: LogLevel | None):
|
|
|
59
61
|
if log_level is None:
|
|
60
62
|
return
|
|
61
63
|
if not isinstance(log_level, LogLevel):
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)
|
|
64
|
+
msg = f"log_level must be a LogLevel enum value, got {log_level} of type {type(log_level)} instead."
|
|
65
|
+
raise TypeError(msg)
|
|
65
66
|
global _LOG_LEVEL
|
|
66
67
|
if log_level != _LOG_LEVEL:
|
|
67
68
|
# Set the loglevel persistenly for subprocesses.
|
|
@@ -89,11 +90,55 @@ def print(msg: str, dedupe: bool = False, **kwargs):
|
|
|
89
90
|
if dedupe:
|
|
90
91
|
if msg in _EMITTED_PRINTS:
|
|
91
92
|
return
|
|
92
|
-
|
|
93
|
-
_EMITTED_PRINTS.add(msg)
|
|
93
|
+
_EMITTED_PRINTS.add(msg)
|
|
94
94
|
_console.print(msg, **kwargs)
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
@once
|
|
98
|
+
def log_file_console():
|
|
99
|
+
"""Create a console that logs to a file.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
A Console object that logs to a file.
|
|
103
|
+
"""
|
|
104
|
+
from reflex.environment import environment
|
|
105
|
+
|
|
106
|
+
if not (env_log_file := environment.REFLEX_LOG_FILE.get()):
|
|
107
|
+
subseconds = int((time.time() % 1) * 1000)
|
|
108
|
+
timestamp = time.strftime("%Y-%m-%d_%H-%M-%S") + f"_{subseconds:03d}"
|
|
109
|
+
log_file = Reflex.DIR / "logs" / (timestamp + ".log")
|
|
110
|
+
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
111
|
+
else:
|
|
112
|
+
log_file = env_log_file
|
|
113
|
+
if log_file.exists():
|
|
114
|
+
log_file.unlink()
|
|
115
|
+
log_file.touch()
|
|
116
|
+
return Console(file=log_file.open("a", encoding="utf-8"))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@once
|
|
120
|
+
def should_use_log_file_console() -> bool:
|
|
121
|
+
"""Check if the log file console should be used.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
True if the log file console should be used, False otherwise.
|
|
125
|
+
"""
|
|
126
|
+
from reflex.environment import environment
|
|
127
|
+
|
|
128
|
+
return environment.REFLEX_ENABLE_FULL_LOGGING.get()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def print_to_log_file(msg: str, dedupe: bool = False, **kwargs):
|
|
132
|
+
"""Print a message to the log file.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
msg: The message to print.
|
|
136
|
+
dedupe: If True, suppress multiple console logs of print message.
|
|
137
|
+
kwargs: Keyword arguments to pass to the print function.
|
|
138
|
+
"""
|
|
139
|
+
log_file_console().print(msg, **kwargs)
|
|
140
|
+
|
|
141
|
+
|
|
97
142
|
def debug(msg: str, dedupe: bool = False, **kwargs):
|
|
98
143
|
"""Print a debug message.
|
|
99
144
|
|
|
@@ -107,12 +152,13 @@ def debug(msg: str, dedupe: bool = False, **kwargs):
|
|
|
107
152
|
if dedupe:
|
|
108
153
|
if msg_ in _EMITTED_DEBUG:
|
|
109
154
|
return
|
|
110
|
-
|
|
111
|
-
_EMITTED_DEBUG.add(msg_)
|
|
155
|
+
_EMITTED_DEBUG.add(msg_)
|
|
112
156
|
if progress := kwargs.pop("progress", None):
|
|
113
157
|
progress.console.print(msg_, **kwargs)
|
|
114
158
|
else:
|
|
115
159
|
print(msg_, **kwargs)
|
|
160
|
+
if should_use_log_file_console() and kwargs.pop("progress", None) is None:
|
|
161
|
+
print_to_log_file(f"[purple]Debug: {msg}[/purple]", **kwargs)
|
|
116
162
|
|
|
117
163
|
|
|
118
164
|
def info(msg: str, dedupe: bool = False, **kwargs):
|
|
@@ -127,9 +173,10 @@ def info(msg: str, dedupe: bool = False, **kwargs):
|
|
|
127
173
|
if dedupe:
|
|
128
174
|
if msg in _EMITTED_INFO:
|
|
129
175
|
return
|
|
130
|
-
|
|
131
|
-
_EMITTED_INFO.add(msg)
|
|
176
|
+
_EMITTED_INFO.add(msg)
|
|
132
177
|
print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
|
|
178
|
+
if should_use_log_file_console():
|
|
179
|
+
print_to_log_file(f"[cyan]Info: {msg}[/cyan]", **kwargs)
|
|
133
180
|
|
|
134
181
|
|
|
135
182
|
def success(msg: str, dedupe: bool = False, **kwargs):
|
|
@@ -144,9 +191,10 @@ def success(msg: str, dedupe: bool = False, **kwargs):
|
|
|
144
191
|
if dedupe:
|
|
145
192
|
if msg in _EMITTED_SUCCESS:
|
|
146
193
|
return
|
|
147
|
-
|
|
148
|
-
_EMITTED_SUCCESS.add(msg)
|
|
194
|
+
_EMITTED_SUCCESS.add(msg)
|
|
149
195
|
print(f"[green]Success: {msg}[/green]", **kwargs)
|
|
196
|
+
if should_use_log_file_console():
|
|
197
|
+
print_to_log_file(f"[green]Success: {msg}[/green]", **kwargs)
|
|
150
198
|
|
|
151
199
|
|
|
152
200
|
def log(msg: str, dedupe: bool = False, **kwargs):
|
|
@@ -161,9 +209,10 @@ def log(msg: str, dedupe: bool = False, **kwargs):
|
|
|
161
209
|
if dedupe:
|
|
162
210
|
if msg in _EMITTED_LOGS:
|
|
163
211
|
return
|
|
164
|
-
|
|
165
|
-
_EMITTED_LOGS.add(msg)
|
|
212
|
+
_EMITTED_LOGS.add(msg)
|
|
166
213
|
_console.log(msg, **kwargs)
|
|
214
|
+
if should_use_log_file_console():
|
|
215
|
+
print_to_log_file(msg, **kwargs)
|
|
167
216
|
|
|
168
217
|
|
|
169
218
|
def rule(title: str, **kwargs):
|
|
@@ -188,9 +237,10 @@ def warn(msg: str, dedupe: bool = False, **kwargs):
|
|
|
188
237
|
if dedupe:
|
|
189
238
|
if msg in _EMIITED_WARNINGS:
|
|
190
239
|
return
|
|
191
|
-
|
|
192
|
-
_EMIITED_WARNINGS.add(msg)
|
|
240
|
+
_EMIITED_WARNINGS.add(msg)
|
|
193
241
|
print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
|
|
242
|
+
if should_use_log_file_console():
|
|
243
|
+
print_to_log_file(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
|
|
194
244
|
|
|
195
245
|
|
|
196
246
|
def _get_first_non_framework_frame() -> FrameType | None:
|
|
@@ -255,6 +305,8 @@ def deprecate(
|
|
|
255
305
|
)
|
|
256
306
|
if _LOG_LEVEL <= LogLevel.WARNING:
|
|
257
307
|
print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
|
|
308
|
+
if should_use_log_file_console():
|
|
309
|
+
print_to_log_file(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
|
|
258
310
|
if dedupe:
|
|
259
311
|
_EMITTED_DEPRECATION_WARNINGS.add(dedupe_key)
|
|
260
312
|
|
|
@@ -271,9 +323,10 @@ def error(msg: str, dedupe: bool = False, **kwargs):
|
|
|
271
323
|
if dedupe:
|
|
272
324
|
if msg in _EMITTED_ERRORS:
|
|
273
325
|
return
|
|
274
|
-
|
|
275
|
-
_EMITTED_ERRORS.add(msg)
|
|
326
|
+
_EMITTED_ERRORS.add(msg)
|
|
276
327
|
print(f"[red]{msg}[/red]", **kwargs)
|
|
328
|
+
if should_use_log_file_console():
|
|
329
|
+
print_to_log_file(f"[red]{msg}[/red]", **kwargs)
|
|
277
330
|
|
|
278
331
|
|
|
279
332
|
def ask(
|
reflex/utils/exec.py
CHANGED
|
@@ -12,13 +12,15 @@ import subprocess
|
|
|
12
12
|
import sys
|
|
13
13
|
from collections.abc import Sequence
|
|
14
14
|
from pathlib import Path
|
|
15
|
+
from typing import NamedTuple, TypedDict
|
|
15
16
|
from urllib.parse import urljoin
|
|
16
17
|
|
|
17
18
|
import psutil
|
|
18
19
|
|
|
19
20
|
from reflex import constants
|
|
20
|
-
from reflex.config import
|
|
21
|
+
from reflex.config import get_config
|
|
21
22
|
from reflex.constants.base import LogLevel
|
|
23
|
+
from reflex.environment import environment
|
|
22
24
|
from reflex.utils import console, path_ops
|
|
23
25
|
from reflex.utils.decorator import once
|
|
24
26
|
from reflex.utils.prerequisites import get_web_dir
|
|
@@ -27,26 +29,102 @@ from reflex.utils.prerequisites import get_web_dir
|
|
|
27
29
|
frontend_process = None
|
|
28
30
|
|
|
29
31
|
|
|
30
|
-
def
|
|
31
|
-
"""
|
|
32
|
+
def get_package_json_and_hash(package_json_path: Path) -> tuple[PackageJson, str]:
|
|
33
|
+
"""Get the content of package.json and its hash.
|
|
32
34
|
|
|
33
35
|
Args:
|
|
34
|
-
|
|
36
|
+
package_json_path: The path to the package.json file.
|
|
35
37
|
|
|
36
38
|
Returns:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Example:
|
|
40
|
-
>>> detect_package_change("package.json")
|
|
41
|
-
'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2'
|
|
39
|
+
A tuple containing the content of package.json as a dictionary and its SHA-256 hash.
|
|
42
40
|
"""
|
|
43
|
-
with
|
|
41
|
+
with package_json_path.open("r") as file:
|
|
44
42
|
json_data = json.load(file)
|
|
45
43
|
|
|
46
44
|
# Calculate the hash
|
|
47
45
|
json_string = json.dumps(json_data, sort_keys=True)
|
|
48
46
|
hash_object = hashlib.sha256(json_string.encode())
|
|
49
|
-
return hash_object.hexdigest()
|
|
47
|
+
return (json_data, hash_object.hexdigest())
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class PackageJson(TypedDict):
|
|
51
|
+
"""package.json content."""
|
|
52
|
+
|
|
53
|
+
dependencies: dict[str, str]
|
|
54
|
+
devDependencies: dict[str, str]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Change(NamedTuple):
|
|
58
|
+
"""A named tuple to represent a change in package dependencies."""
|
|
59
|
+
|
|
60
|
+
added: set[str]
|
|
61
|
+
removed: set[str]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def format_change(name: str, change: Change) -> str:
|
|
65
|
+
"""Format the change for display.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
name: The name of the change (e.g., "dependencies" or "devDependencies").
|
|
69
|
+
change: The Change named tuple containing added and removed packages.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
A formatted string representing the changes.
|
|
73
|
+
"""
|
|
74
|
+
if not change.added and not change.removed:
|
|
75
|
+
return ""
|
|
76
|
+
added_str = ", ".join(sorted(change.added))
|
|
77
|
+
removed_str = ", ".join(sorted(change.removed))
|
|
78
|
+
change_str = f"{name}:\n"
|
|
79
|
+
if change.added:
|
|
80
|
+
change_str += f" Added: {added_str}\n"
|
|
81
|
+
if change.removed:
|
|
82
|
+
change_str += f" Removed: {removed_str}\n"
|
|
83
|
+
return change_str.strip()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_different_packages(
|
|
87
|
+
old_package_json_content: PackageJson,
|
|
88
|
+
new_package_json_content: PackageJson,
|
|
89
|
+
) -> tuple[Change, Change]:
|
|
90
|
+
"""Get the packages that are different between two package JSON contents.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
old_package_json_content: The content of the old package JSON.
|
|
94
|
+
new_package_json_content: The content of the new package JSON.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
A tuple containing two `Change` named tuples:
|
|
98
|
+
- The first `Change` contains the changes in the `dependencies` section.
|
|
99
|
+
- The second `Change` contains the changes in the `devDependencies` section.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def get_changes(old: dict[str, str], new: dict[str, str]) -> Change:
|
|
103
|
+
"""Get the changes between two dictionaries.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
old: The old dictionary of packages.
|
|
107
|
+
new: The new dictionary of packages.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
A `Change` named tuple containing the added and removed packages.
|
|
111
|
+
"""
|
|
112
|
+
old_keys = set(old.keys())
|
|
113
|
+
new_keys = set(new.keys())
|
|
114
|
+
added = new_keys - old_keys
|
|
115
|
+
removed = old_keys - new_keys
|
|
116
|
+
return Change(added=added, removed=removed)
|
|
117
|
+
|
|
118
|
+
dependencies_change = get_changes(
|
|
119
|
+
old_package_json_content.get("dependencies", {}),
|
|
120
|
+
new_package_json_content.get("dependencies", {}),
|
|
121
|
+
)
|
|
122
|
+
dev_dependencies_change = get_changes(
|
|
123
|
+
old_package_json_content.get("devDependencies", {}),
|
|
124
|
+
new_package_json_content.get("devDependencies", {}),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return dependencies_change, dev_dependencies_change
|
|
50
128
|
|
|
51
129
|
|
|
52
130
|
def kill(proc_pid: int):
|
|
@@ -86,7 +164,7 @@ def run_process_and_launch_url(
|
|
|
86
164
|
from reflex.utils import processes
|
|
87
165
|
|
|
88
166
|
json_file_path = get_web_dir() / constants.PackageJson.PATH
|
|
89
|
-
last_hash =
|
|
167
|
+
last_content, last_hash = get_package_json_and_hash(json_file_path)
|
|
90
168
|
process = None
|
|
91
169
|
first_run = True
|
|
92
170
|
|
|
@@ -105,6 +183,18 @@ def run_process_and_launch_url(
|
|
|
105
183
|
frontend_process = process
|
|
106
184
|
if process.stdout:
|
|
107
185
|
for line in processes.stream_logs("Starting frontend", process):
|
|
186
|
+
new_content, new_hash = get_package_json_and_hash(json_file_path)
|
|
187
|
+
if new_hash != last_hash:
|
|
188
|
+
dependencies_change, dev_dependencies_change = (
|
|
189
|
+
get_different_packages(last_content, new_content)
|
|
190
|
+
)
|
|
191
|
+
last_content, last_hash = new_content, new_hash
|
|
192
|
+
console.info(
|
|
193
|
+
"Detected changes in package.json.\n"
|
|
194
|
+
+ format_change("Dependencies", dependencies_change)
|
|
195
|
+
+ format_change("Dev Dependencies", dev_dependencies_change)
|
|
196
|
+
)
|
|
197
|
+
|
|
108
198
|
match = re.search(constants.Next.FRONTEND_LISTENING_REGEX, line)
|
|
109
199
|
if match:
|
|
110
200
|
if first_run:
|
|
@@ -119,22 +209,8 @@ def run_process_and_launch_url(
|
|
|
119
209
|
notify_backend()
|
|
120
210
|
first_run = False
|
|
121
211
|
else:
|
|
122
|
-
console.print("
|
|
123
|
-
|
|
124
|
-
if any(
|
|
125
|
-
x in line for x in ("bin executable does not exist on disk",)
|
|
126
|
-
):
|
|
127
|
-
console.error(
|
|
128
|
-
"Try setting `REFLEX_USE_NPM=1` and re-running `reflex init` and `reflex run` to use npm instead of bun:\n"
|
|
129
|
-
"`REFLEX_USE_NPM=1 reflex init`\n"
|
|
130
|
-
"`REFLEX_USE_NPM=1 reflex run`"
|
|
131
|
-
)
|
|
132
|
-
new_hash = detect_package_change(json_file_path)
|
|
133
|
-
if new_hash != last_hash:
|
|
134
|
-
last_hash = new_hash
|
|
135
|
-
kill(process.pid)
|
|
136
|
-
process = None
|
|
137
|
-
break # for line in process.stdout
|
|
212
|
+
console.print("Frontend is restarting...")
|
|
213
|
+
|
|
138
214
|
if process is not None:
|
|
139
215
|
break # while True
|
|
140
216
|
|
|
@@ -244,14 +320,12 @@ def get_app_file() -> Path:
|
|
|
244
320
|
sys.path.insert(0, current_working_dir)
|
|
245
321
|
module_spec = importlib.util.find_spec(get_app_module())
|
|
246
322
|
if module_spec is None:
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
)
|
|
323
|
+
msg = f"Module {get_app_module()} not found. Make sure the module is installed."
|
|
324
|
+
raise ImportError(msg)
|
|
250
325
|
file_name = module_spec.origin
|
|
251
326
|
if file_name is None:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
)
|
|
327
|
+
msg = f"Module {get_app_module()} not found. Make sure the module is installed."
|
|
328
|
+
raise ImportError(msg)
|
|
255
329
|
return Path(file_name).resolve()
|
|
256
330
|
|
|
257
331
|
|
|
@@ -300,13 +374,12 @@ def get_reload_paths() -> Sequence[Path]:
|
|
|
300
374
|
The reload paths for the backend.
|
|
301
375
|
"""
|
|
302
376
|
config = get_config()
|
|
303
|
-
reload_paths = [Path(
|
|
304
|
-
if config.
|
|
305
|
-
module_path = Path(
|
|
377
|
+
reload_paths = [Path.cwd()]
|
|
378
|
+
if (spec := importlib.util.find_spec(config.module)) is not None and spec.origin:
|
|
379
|
+
module_path = Path(spec.origin).resolve().parent
|
|
306
380
|
|
|
307
381
|
while module_path.parent.name and any(
|
|
308
|
-
sibling_file.name == "__init__.py"
|
|
309
|
-
for sibling_file in module_path.parent.iterdir()
|
|
382
|
+
sibling_file.name == "__init__.py" for sibling_file in module_path.iterdir()
|
|
310
383
|
):
|
|
311
384
|
# go up a level to find dir without `__init__.py`
|
|
312
385
|
module_path = module_path.parent
|
|
@@ -383,6 +456,7 @@ HOTRELOAD_IGNORE_EXTENSIONS = (
|
|
|
383
456
|
"json",
|
|
384
457
|
"sh",
|
|
385
458
|
"bash",
|
|
459
|
+
"log",
|
|
386
460
|
)
|
|
387
461
|
|
|
388
462
|
HOTRELOAD_IGNORE_PATTERNS = (
|
reflex/utils/export.py
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from reflex import constants
|
|
6
|
-
from reflex.config import
|
|
6
|
+
from reflex.config import get_config
|
|
7
|
+
from reflex.environment import environment
|
|
7
8
|
from reflex.utils import build, console, exec, prerequisites, telemetry
|
|
8
9
|
|
|
9
10
|
|