reflex 0.7.14a5__py3-none-any.whl → 0.8.0__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/app/rxconfig.py.jinja2 +4 -1
- reflex/.templates/jinja/web/package.json.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +21 -11
- reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
- reflex/.templates/jinja/web/styles/styles.css.jinja2 +1 -0
- reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -8
- reflex/.templates/web/app/entry.client.js +8 -0
- reflex/.templates/web/app/routes.js +10 -0
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -37
- reflex/.templates/web/postcss.config.js +1 -1
- reflex/.templates/web/react-router.config.js +6 -0
- reflex/.templates/web/styles/__reflex_style_reset.css +399 -0
- reflex/.templates/web/utils/client_side_routing.js +21 -19
- reflex/.templates/web/utils/react-theme.js +92 -0
- reflex/.templates/web/utils/state.js +251 -100
- reflex/.templates/web/vite-plugin-safari-cachebust.js +160 -0
- reflex/.templates/web/vite.config.js +39 -0
- reflex/__init__.py +1 -6
- reflex/__init__.pyi +327 -192
- reflex/app.py +103 -152
- reflex/base.py +1 -87
- reflex/compiler/compiler.py +70 -19
- reflex/compiler/templates.py +3 -3
- reflex/compiler/utils.py +91 -33
- reflex/components/__init__.py +0 -2
- reflex/components/__init__.pyi +34 -18
- reflex/components/base/__init__.py +1 -5
- reflex/components/base/__init__.pyi +30 -21
- reflex/components/base/app_wrap.pyi +7 -7
- reflex/components/base/body.pyi +7 -7
- reflex/components/base/document.py +18 -14
- reflex/components/base/document.pyi +88 -38
- reflex/components/base/error_boundary.pyi +7 -7
- reflex/components/base/fragment.pyi +7 -7
- reflex/components/base/link.pyi +12 -12
- reflex/components/base/meta.py +4 -15
- reflex/components/base/meta.pyi +31 -31
- reflex/components/base/script.py +60 -58
- reflex/components/base/script.pyi +248 -34
- reflex/components/base/strict_mode.pyi +7 -7
- reflex/components/component.py +146 -217
- reflex/components/core/__init__.py +1 -0
- reflex/components/core/__init__.pyi +77 -37
- reflex/components/core/auto_scroll.pyi +7 -7
- reflex/components/core/banner.pyi +33 -33
- reflex/components/core/client_side_routing.py +7 -6
- reflex/components/core/client_side_routing.pyi +8 -59
- reflex/components/core/clipboard.pyi +7 -7
- reflex/components/core/debounce.py +1 -0
- reflex/components/core/debounce.pyi +7 -7
- reflex/components/core/foreach.py +5 -4
- reflex/components/core/helmet.py +14 -0
- reflex/components/{next/base.pyi → core/helmet.pyi} +12 -10
- reflex/components/core/html.pyi +7 -7
- reflex/components/core/match.py +3 -3
- reflex/components/core/sticky.pyi +21 -20
- reflex/components/core/upload.py +4 -2
- reflex/components/core/upload.pyi +26 -25
- reflex/components/datadisplay/__init__.pyi +13 -7
- reflex/components/datadisplay/code.py +14 -79
- reflex/components/datadisplay/code.pyi +11 -13
- reflex/components/datadisplay/dataeditor.pyi +38 -15
- reflex/components/datadisplay/shiki_code_block.py +5 -3
- reflex/components/datadisplay/shiki_code_block.pyi +16 -15
- reflex/components/dynamic.py +5 -5
- reflex/components/el/__init__.pyi +506 -246
- reflex/components/el/element.pyi +7 -7
- reflex/components/el/elements/__init__.pyi +504 -245
- reflex/components/el/elements/base.pyi +7 -7
- reflex/components/el/elements/forms.pyi +146 -101
- reflex/components/el/elements/inline.pyi +142 -142
- reflex/components/el/elements/media.pyi +131 -130
- reflex/components/el/elements/metadata.pyi +32 -32
- reflex/components/el/elements/other.pyi +37 -37
- reflex/components/el/elements/scripts.pyi +17 -17
- reflex/components/el/elements/sectioning.pyi +77 -77
- reflex/components/el/elements/tables.pyi +52 -52
- reflex/components/el/elements/typography.pyi +77 -77
- reflex/components/field.py +175 -0
- reflex/components/gridjs/datatable.py +2 -2
- reflex/components/gridjs/datatable.pyi +14 -14
- reflex/components/lucide/icon.py +6 -2
- reflex/components/lucide/icon.pyi +19 -17
- reflex/components/markdown/markdown.py +5 -3
- reflex/components/markdown/markdown.pyi +7 -7
- reflex/components/moment/moment.py +1 -1
- reflex/components/moment/moment.pyi +7 -7
- reflex/components/plotly/plotly.py +12 -6
- reflex/components/plotly/plotly.pyi +50 -49
- reflex/components/props.py +376 -27
- reflex/components/radix/__init__.pyi +123 -65
- reflex/components/radix/primitives/__init__.pyi +6 -4
- reflex/components/radix/primitives/accordion.py +8 -1
- reflex/components/radix/primitives/accordion.pyi +37 -37
- reflex/components/radix/primitives/base.pyi +12 -12
- reflex/components/radix/primitives/drawer.pyi +56 -55
- reflex/components/radix/primitives/form.pyi +63 -53
- reflex/components/radix/primitives/progress.pyi +26 -25
- reflex/components/radix/primitives/slider.pyi +27 -27
- reflex/components/radix/themes/__init__.pyi +5 -6
- reflex/components/radix/themes/base.py +3 -3
- reflex/components/radix/themes/base.pyi +42 -42
- reflex/components/radix/themes/color_mode.py +5 -6
- reflex/components/radix/themes/color_mode.pyi +17 -17
- reflex/components/radix/themes/components/__init__.pyi +75 -38
- reflex/components/radix/themes/components/alert_dialog.pyi +37 -37
- reflex/components/radix/themes/components/aspect_ratio.pyi +7 -7
- reflex/components/radix/themes/components/avatar.pyi +7 -7
- reflex/components/radix/themes/components/badge.pyi +7 -7
- reflex/components/radix/themes/components/button.pyi +7 -7
- reflex/components/radix/themes/components/callout.pyi +26 -25
- reflex/components/radix/themes/components/card.pyi +7 -7
- reflex/components/radix/themes/components/checkbox.pyi +16 -15
- reflex/components/radix/themes/components/checkbox_cards.pyi +12 -12
- reflex/components/radix/themes/components/checkbox_group.pyi +12 -12
- reflex/components/radix/themes/components/context_menu.pyi +67 -67
- reflex/components/radix/themes/components/data_list.pyi +22 -22
- reflex/components/radix/themes/components/dialog.pyi +36 -35
- reflex/components/radix/themes/components/dropdown_menu.pyi +42 -42
- reflex/components/radix/themes/components/hover_card.pyi +21 -20
- reflex/components/radix/themes/components/icon_button.pyi +7 -7
- reflex/components/radix/themes/components/inset.pyi +7 -7
- reflex/components/radix/themes/components/popover.pyi +22 -22
- reflex/components/radix/themes/components/progress.pyi +7 -7
- reflex/components/radix/themes/components/radio.pyi +7 -7
- reflex/components/radix/themes/components/radio_cards.pyi +12 -12
- reflex/components/radix/themes/components/radio_group.pyi +21 -20
- reflex/components/radix/themes/components/scroll_area.pyi +7 -7
- reflex/components/radix/themes/components/segmented_control.pyi +12 -12
- reflex/components/radix/themes/components/select.pyi +46 -45
- reflex/components/radix/themes/components/separator.pyi +7 -7
- reflex/components/radix/themes/components/skeleton.pyi +7 -7
- reflex/components/radix/themes/components/slider.pyi +17 -9
- reflex/components/radix/themes/components/spinner.pyi +7 -7
- reflex/components/radix/themes/components/switch.pyi +7 -7
- reflex/components/radix/themes/components/table.pyi +37 -37
- reflex/components/radix/themes/components/tabs.pyi +26 -25
- reflex/components/radix/themes/components/text_area.pyi +15 -9
- reflex/components/radix/themes/components/text_field.pyi +32 -19
- reflex/components/radix/themes/components/tooltip.pyi +7 -7
- reflex/components/radix/themes/layout/__init__.pyi +27 -14
- reflex/components/radix/themes/layout/base.pyi +7 -7
- reflex/components/radix/themes/layout/box.pyi +7 -7
- reflex/components/radix/themes/layout/center.pyi +7 -7
- reflex/components/radix/themes/layout/container.pyi +7 -7
- reflex/components/radix/themes/layout/flex.pyi +7 -7
- reflex/components/radix/themes/layout/grid.pyi +7 -7
- reflex/components/radix/themes/layout/list.pyi +26 -25
- reflex/components/radix/themes/layout/section.pyi +7 -7
- reflex/components/radix/themes/layout/spacer.pyi +7 -7
- reflex/components/radix/themes/layout/stack.pyi +17 -17
- reflex/components/radix/themes/typography/__init__.pyi +7 -5
- reflex/components/radix/themes/typography/blockquote.pyi +7 -7
- reflex/components/radix/themes/typography/code.pyi +7 -7
- reflex/components/radix/themes/typography/heading.pyi +7 -7
- reflex/components/radix/themes/typography/link.py +46 -11
- reflex/components/radix/themes/typography/link.pyi +312 -9
- reflex/components/radix/themes/typography/text.pyi +36 -35
- reflex/components/react_player/audio.pyi +10 -8
- reflex/components/react_player/react_player.pyi +7 -7
- reflex/components/react_player/video.pyi +10 -8
- reflex/components/recharts/__init__.pyi +208 -100
- reflex/components/recharts/cartesian.py +10 -8
- reflex/components/recharts/cartesian.pyi +90 -94
- reflex/components/recharts/charts.py +4 -2
- reflex/components/recharts/charts.pyi +49 -49
- reflex/components/recharts/general.pyi +31 -31
- reflex/components/recharts/polar.py +8 -4
- reflex/components/recharts/polar.pyi +23 -23
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/recharts/recharts.pyi +12 -12
- reflex/components/sonner/toast.py +3 -3
- reflex/components/sonner/toast.pyi +9 -9
- reflex/config.py +10 -113
- reflex/constants/__init__.py +2 -2
- reflex/constants/base.py +28 -11
- reflex/constants/compiler.py +12 -3
- reflex/constants/event.py +1 -0
- reflex/constants/installer.py +26 -20
- reflex/constants/route.py +27 -8
- reflex/constants/state.py +2 -0
- reflex/custom_components/custom_components.py +0 -14
- reflex/environment.py +77 -5
- reflex/event.py +178 -81
- reflex/experimental/__init__.py +0 -30
- reflex/istate/__init__.py +69 -0
- reflex/istate/manager.py +1 -0
- reflex/istate/proxy.py +5 -3
- reflex/page.py +0 -27
- reflex/plugins/__init__.py +3 -2
- reflex/plugins/base.py +5 -1
- reflex/plugins/shared_tailwind.py +215 -0
- reflex/plugins/sitemap.py +206 -0
- reflex/plugins/tailwind_v3.py +15 -108
- reflex/plugins/tailwind_v4.py +18 -110
- reflex/reflex.py +1 -0
- reflex/route.py +157 -75
- reflex/state.py +171 -155
- reflex/testing.py +86 -16
- reflex/utils/build.py +38 -82
- reflex/utils/exec.py +83 -175
- reflex/utils/export.py +2 -2
- reflex/utils/format.py +1 -5
- reflex/utils/imports.py +5 -16
- reflex/utils/misc.py +67 -0
- reflex/utils/prerequisites.py +66 -68
- reflex/utils/processes.py +24 -47
- reflex/utils/pyi_generator.py +44 -49
- reflex/utils/serializers.py +14 -1
- reflex/utils/telemetry.py +0 -15
- reflex/utils/types.py +197 -62
- reflex/vars/__init__.py +2 -0
- reflex/vars/base.py +367 -134
- {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/METADATA +15 -8
- reflex-0.8.0.dist-info/RECORD +403 -0
- reflex/.templates/web/next.config.js +0 -7
- reflex/components/base/head.py +0 -20
- reflex/components/base/head.pyi +0 -116
- reflex/components/next/__init__.py +0 -10
- reflex/components/next/base.py +0 -7
- reflex/components/next/image.py +0 -117
- reflex/components/next/image.pyi +0 -94
- reflex/components/next/link.py +0 -20
- reflex/components/next/link.pyi +0 -67
- reflex/components/next/video.py +0 -38
- reflex/components/next/video.pyi +0 -68
- reflex/components/suneditor/__init__.py +0 -5
- reflex/components/suneditor/editor.py +0 -269
- reflex/components/suneditor/editor.pyi +0 -199
- reflex/experimental/layout.py +0 -254
- reflex-0.7.14a5.dist-info/RECORD +0 -407
- {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/WHEEL +0 -0
- {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/licenses/LICENSE +0 -0
reflex/utils/exec.py
CHANGED
|
@@ -12,17 +12,16 @@ 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
|
+
from typing import Any, NamedTuple, TypedDict
|
|
16
16
|
from urllib.parse import urljoin
|
|
17
17
|
|
|
18
|
-
import psutil
|
|
19
|
-
|
|
20
18
|
from reflex import constants
|
|
21
19
|
from reflex.config import get_config
|
|
22
20
|
from reflex.constants.base import LogLevel
|
|
23
21
|
from reflex.environment import environment
|
|
24
22
|
from reflex.utils import console, path_ops
|
|
25
23
|
from reflex.utils.decorator import once
|
|
24
|
+
from reflex.utils.misc import get_module_path
|
|
26
25
|
from reflex.utils.prerequisites import get_web_dir
|
|
27
26
|
|
|
28
27
|
# For uvicorn windows bug fix (#2335)
|
|
@@ -130,12 +129,16 @@ def get_different_packages(
|
|
|
130
129
|
def kill(proc_pid: int):
|
|
131
130
|
"""Kills a process and all its child processes.
|
|
132
131
|
|
|
132
|
+
Requires the `psutil` library to be installed.
|
|
133
|
+
|
|
133
134
|
Args:
|
|
134
|
-
proc_pid
|
|
135
|
+
proc_pid: The process ID of the process to be killed.
|
|
135
136
|
|
|
136
137
|
Example:
|
|
137
138
|
>>> kill(1234)
|
|
138
139
|
"""
|
|
140
|
+
import psutil
|
|
141
|
+
|
|
139
142
|
process = psutil.Process(proc_pid)
|
|
140
143
|
for proc in process.children(recursive=True):
|
|
141
144
|
proc.kill()
|
|
@@ -170,7 +173,12 @@ def run_process_and_launch_url(
|
|
|
170
173
|
|
|
171
174
|
while True:
|
|
172
175
|
if process is None:
|
|
173
|
-
kwargs = {
|
|
176
|
+
kwargs: dict[str, Any] = {
|
|
177
|
+
"env": {
|
|
178
|
+
**os.environ,
|
|
179
|
+
"NO_COLOR": "1",
|
|
180
|
+
}
|
|
181
|
+
}
|
|
174
182
|
if constants.IS_WINDOWS and backend_present:
|
|
175
183
|
kwargs["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # pyright: ignore [reportAttributeAccessIssue]
|
|
176
184
|
process = processes.new_process(
|
|
@@ -195,7 +203,7 @@ def run_process_and_launch_url(
|
|
|
195
203
|
+ format_change("Dev Dependencies", dev_dependencies_change)
|
|
196
204
|
)
|
|
197
205
|
|
|
198
|
-
match = re.search(constants.
|
|
206
|
+
match = re.search(constants.ReactRouter.FRONTEND_LISTENING_REGEX, line)
|
|
199
207
|
if match:
|
|
200
208
|
if first_run:
|
|
201
209
|
url = match.group(1)
|
|
@@ -318,15 +326,12 @@ def get_app_file() -> Path:
|
|
|
318
326
|
if current_working_dir not in sys.path:
|
|
319
327
|
# Add the current working directory to sys.path
|
|
320
328
|
sys.path.insert(0, current_working_dir)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
329
|
+
app_module = get_app_module()
|
|
330
|
+
module_path = get_module_path(app_module)
|
|
331
|
+
if module_path is None:
|
|
332
|
+
msg = f"Module {app_module} not found. Make sure the module is installed."
|
|
324
333
|
raise ImportError(msg)
|
|
325
|
-
|
|
326
|
-
if file_name is None:
|
|
327
|
-
msg = f"Module {get_app_module()} not found. Make sure the module is installed."
|
|
328
|
-
raise ImportError(msg)
|
|
329
|
-
return Path(file_name).resolve()
|
|
334
|
+
return module_path
|
|
330
335
|
|
|
331
336
|
|
|
332
337
|
def get_app_instance_from_file() -> str:
|
|
@@ -362,26 +367,58 @@ def run_backend(
|
|
|
362
367
|
|
|
363
368
|
# Run the backend in development mode.
|
|
364
369
|
if should_use_granian():
|
|
370
|
+
# We import reflex app because this lets granian cache the module
|
|
371
|
+
import reflex.app # noqa: F401
|
|
372
|
+
|
|
365
373
|
run_granian_backend(host, port, loglevel)
|
|
366
374
|
else:
|
|
367
375
|
run_uvicorn_backend(host, port, loglevel)
|
|
368
376
|
|
|
369
377
|
|
|
378
|
+
def _has_child_file(directory: Path, file_name: str) -> bool:
|
|
379
|
+
"""Check if a directory has a child file with the given name.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
directory: The directory to check.
|
|
383
|
+
file_name: The name of the file to look for.
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
True if the directory has a child file with the given name, False otherwise.
|
|
387
|
+
"""
|
|
388
|
+
return any(child_file.name == file_name for child_file in directory.iterdir())
|
|
389
|
+
|
|
390
|
+
|
|
370
391
|
def get_reload_paths() -> Sequence[Path]:
|
|
371
392
|
"""Get the reload paths for the backend.
|
|
372
393
|
|
|
373
394
|
Returns:
|
|
374
395
|
The reload paths for the backend.
|
|
396
|
+
|
|
397
|
+
Raises:
|
|
398
|
+
RuntimeError: If the `__init__.py` file is found in the app root directory.
|
|
375
399
|
"""
|
|
376
400
|
config = get_config()
|
|
377
401
|
reload_paths = [Path.cwd()]
|
|
378
|
-
|
|
379
|
-
|
|
402
|
+
app_module = config.module
|
|
403
|
+
module_path = get_module_path(app_module)
|
|
404
|
+
if module_path is not None:
|
|
405
|
+
module_path = module_path.parent
|
|
406
|
+
|
|
407
|
+
while module_path.parent.name and _has_child_file(module_path, "__init__.py"):
|
|
408
|
+
if _has_child_file(module_path, "rxconfig.py"):
|
|
409
|
+
init_file = module_path / "__init__.py"
|
|
410
|
+
init_file_content = init_file.read_text()
|
|
411
|
+
if init_file_content.strip():
|
|
412
|
+
msg = "There should not be an `__init__.py` file in your app root directory"
|
|
413
|
+
raise RuntimeError(msg)
|
|
414
|
+
console.warn(
|
|
415
|
+
"Removing `__init__.py` file in the app root directory. "
|
|
416
|
+
"This file can cause issues with module imports. "
|
|
417
|
+
)
|
|
418
|
+
init_file.unlink()
|
|
419
|
+
break
|
|
380
420
|
|
|
381
|
-
|
|
382
|
-
sibling_file.name == "__init__.py" for sibling_file in module_path.iterdir()
|
|
383
|
-
):
|
|
384
|
-
# go up a level to find dir without `__init__.py`
|
|
421
|
+
# go up a level to find dir without `__init__.py` or with `rxconfig.py`
|
|
385
422
|
module_path = module_path.parent
|
|
386
423
|
|
|
387
424
|
reload_paths = [module_path]
|
|
@@ -482,9 +519,11 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
|
|
|
482
519
|
|
|
483
520
|
from granian.constants import Interfaces
|
|
484
521
|
from granian.log import LogLevels
|
|
485
|
-
from granian.server import
|
|
522
|
+
from granian.server import Server as Granian
|
|
486
523
|
|
|
487
|
-
|
|
524
|
+
from reflex.environment import _paths_from_environment
|
|
525
|
+
|
|
526
|
+
granian_app = Granian(
|
|
488
527
|
target=get_app_instance_from_file(),
|
|
489
528
|
factory=True,
|
|
490
529
|
address=host,
|
|
@@ -496,76 +535,11 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
|
|
|
496
535
|
reload_ignore_worker_failure=True,
|
|
497
536
|
reload_ignore_patterns=HOTRELOAD_IGNORE_PATTERNS,
|
|
498
537
|
reload_tick=100,
|
|
538
|
+
env_files=_paths_from_environment() or None,
|
|
499
539
|
workers_kill_timeout=2,
|
|
500
|
-
).serve()
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
def _deprecate_asgi_config(
|
|
504
|
-
config_name: str,
|
|
505
|
-
reason: str = "",
|
|
506
|
-
):
|
|
507
|
-
console.deprecate(
|
|
508
|
-
f"config.{config_name}",
|
|
509
|
-
reason=reason,
|
|
510
|
-
deprecation_version="0.7.9",
|
|
511
|
-
removal_version="0.8.0",
|
|
512
540
|
)
|
|
513
541
|
|
|
514
|
-
|
|
515
|
-
@once
|
|
516
|
-
def _get_backend_workers():
|
|
517
|
-
from reflex.utils import processes
|
|
518
|
-
|
|
519
|
-
config = get_config()
|
|
520
|
-
|
|
521
|
-
gunicorn_workers = config.gunicorn_workers or 0
|
|
522
|
-
|
|
523
|
-
if config.gunicorn_workers is not None:
|
|
524
|
-
_deprecate_asgi_config(
|
|
525
|
-
"gunicorn_workers",
|
|
526
|
-
"If you're using Granian, use GRANIAN_WORKERS instead.",
|
|
527
|
-
)
|
|
528
|
-
|
|
529
|
-
return gunicorn_workers if gunicorn_workers else processes.get_num_workers()
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
@once
|
|
533
|
-
def _get_backend_timeout():
|
|
534
|
-
config = get_config()
|
|
535
|
-
|
|
536
|
-
timeout = config.timeout or 120
|
|
537
|
-
|
|
538
|
-
if config.timeout is not None:
|
|
539
|
-
_deprecate_asgi_config(
|
|
540
|
-
"timeout",
|
|
541
|
-
"If you're using Granian, use GRANIAN_WORKERS_LIFETIME instead.",
|
|
542
|
-
)
|
|
543
|
-
|
|
544
|
-
return timeout
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
@once
|
|
548
|
-
def _get_backend_max_requests():
|
|
549
|
-
config = get_config()
|
|
550
|
-
|
|
551
|
-
gunicorn_max_requests = config.gunicorn_max_requests or 120
|
|
552
|
-
|
|
553
|
-
if config.gunicorn_max_requests is not None:
|
|
554
|
-
_deprecate_asgi_config("gunicorn_max_requests")
|
|
555
|
-
|
|
556
|
-
return gunicorn_max_requests
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
@once
|
|
560
|
-
def _get_backend_max_requests_jitter():
|
|
561
|
-
config = get_config()
|
|
562
|
-
|
|
563
|
-
gunicorn_max_requests_jitter = config.gunicorn_max_requests_jitter or 25
|
|
564
|
-
|
|
565
|
-
if config.gunicorn_max_requests_jitter is not None:
|
|
566
|
-
_deprecate_asgi_config("gunicorn_max_requests_jitter")
|
|
567
|
-
|
|
568
|
-
return gunicorn_max_requests_jitter
|
|
542
|
+
granian_app.serve()
|
|
569
543
|
|
|
570
544
|
|
|
571
545
|
def run_backend_prod(
|
|
@@ -601,72 +575,12 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|
|
601
575
|
"""
|
|
602
576
|
from reflex.utils import processes
|
|
603
577
|
|
|
604
|
-
config = get_config()
|
|
605
|
-
|
|
606
578
|
app_module = get_app_instance()
|
|
607
579
|
|
|
608
580
|
command = (
|
|
609
|
-
[
|
|
610
|
-
"uvicorn",
|
|
611
|
-
*(
|
|
612
|
-
(
|
|
613
|
-
"--limit-max-requests",
|
|
614
|
-
str(max_requessts),
|
|
615
|
-
)
|
|
616
|
-
if (
|
|
617
|
-
(max_requessts := _get_backend_max_requests()) is not None
|
|
618
|
-
and max_requessts > 0
|
|
619
|
-
)
|
|
620
|
-
else ()
|
|
621
|
-
),
|
|
622
|
-
*(
|
|
623
|
-
("--timeout-keep-alive", str(timeout))
|
|
624
|
-
if (timeout := _get_backend_timeout()) is not None
|
|
625
|
-
else ()
|
|
626
|
-
),
|
|
627
|
-
*("--host", host),
|
|
628
|
-
*("--port", str(port)),
|
|
629
|
-
*("--workers", str(_get_backend_workers())),
|
|
630
|
-
"--factory",
|
|
631
|
-
app_module,
|
|
632
|
-
]
|
|
581
|
+
["uvicorn", *("--host", host), *("--port", str(port)), "--factory", app_module]
|
|
633
582
|
if constants.IS_WINDOWS
|
|
634
|
-
else [
|
|
635
|
-
"gunicorn",
|
|
636
|
-
*("--worker-class", config.gunicorn_worker_class),
|
|
637
|
-
*(
|
|
638
|
-
(
|
|
639
|
-
"--max-requests",
|
|
640
|
-
str(max_requessts),
|
|
641
|
-
)
|
|
642
|
-
if (
|
|
643
|
-
(max_requessts := _get_backend_max_requests()) is not None
|
|
644
|
-
and max_requessts > 0
|
|
645
|
-
)
|
|
646
|
-
else ()
|
|
647
|
-
),
|
|
648
|
-
*(
|
|
649
|
-
(
|
|
650
|
-
"--max-requests-jitter",
|
|
651
|
-
str(max_requessts_jitter),
|
|
652
|
-
)
|
|
653
|
-
if (
|
|
654
|
-
(max_requessts_jitter := _get_backend_max_requests_jitter())
|
|
655
|
-
is not None
|
|
656
|
-
and max_requessts_jitter > 0
|
|
657
|
-
)
|
|
658
|
-
else ()
|
|
659
|
-
),
|
|
660
|
-
"--preload",
|
|
661
|
-
*(
|
|
662
|
-
("--timeout", str(timeout))
|
|
663
|
-
if (timeout := _get_backend_timeout()) is not None
|
|
664
|
-
else ()
|
|
665
|
-
),
|
|
666
|
-
*("--bind", f"{host}:{port}"),
|
|
667
|
-
*("--threads", str(_get_backend_workers())),
|
|
668
|
-
f"{app_module}()",
|
|
669
|
-
]
|
|
583
|
+
else ["gunicorn", "--preload", *("--bind", f"{host}:{port}"), f"{app_module}()"]
|
|
670
584
|
)
|
|
671
585
|
|
|
672
586
|
command += [
|
|
@@ -691,32 +605,26 @@ def run_granian_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|
|
691
605
|
port: The app port
|
|
692
606
|
loglevel: The log level.
|
|
693
607
|
"""
|
|
608
|
+
from granian.constants import Interfaces
|
|
609
|
+
|
|
694
610
|
from reflex.utils import processes
|
|
695
611
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
env={
|
|
713
|
-
environment.REFLEX_SKIP_COMPILE.name: "true"
|
|
714
|
-
}, # skip compile for prod backend
|
|
715
|
-
)
|
|
716
|
-
except ImportError:
|
|
717
|
-
console.error(
|
|
718
|
-
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian[reload]>=1.6.0"`)'
|
|
719
|
-
)
|
|
612
|
+
command = [
|
|
613
|
+
"granian",
|
|
614
|
+
*("--log-level", "critical"),
|
|
615
|
+
*("--host", host),
|
|
616
|
+
*("--port", str(port)),
|
|
617
|
+
*("--interface", str(Interfaces.ASGI)),
|
|
618
|
+
*("--factory", get_app_instance_from_file()),
|
|
619
|
+
]
|
|
620
|
+
processes.new_process(
|
|
621
|
+
command,
|
|
622
|
+
run=True,
|
|
623
|
+
show_logs=True,
|
|
624
|
+
env={
|
|
625
|
+
environment.REFLEX_SKIP_COMPILE.name: "true"
|
|
626
|
+
}, # skip compile for prod backend
|
|
627
|
+
)
|
|
720
628
|
|
|
721
629
|
|
|
722
630
|
def output_system_info():
|
reflex/utils/export.py
CHANGED
|
@@ -56,13 +56,13 @@ def export(
|
|
|
56
56
|
|
|
57
57
|
if frontend:
|
|
58
58
|
# Ensure module can be imported and app.compile() is called.
|
|
59
|
-
prerequisites.get_compiled_app(
|
|
59
|
+
prerequisites.get_compiled_app(prerender_routes=True)
|
|
60
60
|
# Set up .web directory and install frontend dependencies.
|
|
61
61
|
build.setup_frontend(Path.cwd())
|
|
62
62
|
|
|
63
63
|
# Build the static app.
|
|
64
64
|
if frontend:
|
|
65
|
-
build.build(
|
|
65
|
+
build.build()
|
|
66
66
|
|
|
67
67
|
# Zip up the app.
|
|
68
68
|
if zipping:
|
reflex/utils/format.py
CHANGED
|
@@ -310,20 +310,16 @@ def format_var(var: Var) -> str:
|
|
|
310
310
|
return str(var)
|
|
311
311
|
|
|
312
312
|
|
|
313
|
-
def format_route(route: str
|
|
313
|
+
def format_route(route: str) -> str:
|
|
314
314
|
"""Format the given route.
|
|
315
315
|
|
|
316
316
|
Args:
|
|
317
317
|
route: The route to format.
|
|
318
|
-
format_case: whether to format case to kebab case.
|
|
319
318
|
|
|
320
319
|
Returns:
|
|
321
320
|
The formatted route.
|
|
322
321
|
"""
|
|
323
322
|
route = route.strip("/")
|
|
324
|
-
# Strip the route and format casing.
|
|
325
|
-
if format_case:
|
|
326
|
-
route = to_kebab_case(route)
|
|
327
323
|
|
|
328
324
|
# If the route is empty, return the index route.
|
|
329
325
|
if route == "":
|
reflex/utils/imports.py
CHANGED
|
@@ -52,19 +52,12 @@ def parse_imports(
|
|
|
52
52
|
Returns:
|
|
53
53
|
The parsed import dict.
|
|
54
54
|
"""
|
|
55
|
-
|
|
56
|
-
def _make_list(
|
|
57
|
-
value: ImmutableImportTypes,
|
|
58
|
-
) -> list[str | ImportVar] | list[ImportVar]:
|
|
59
|
-
if isinstance(value, (str, ImportVar)):
|
|
60
|
-
return [value]
|
|
61
|
-
return list(value)
|
|
62
|
-
|
|
63
55
|
return {
|
|
64
|
-
package: [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
package: [maybe_tags]
|
|
57
|
+
if isinstance(maybe_tags, ImportVar)
|
|
58
|
+
else [ImportVar(tag=maybe_tags)]
|
|
59
|
+
if isinstance(maybe_tags, str)
|
|
60
|
+
else [ImportVar(tag=tag) if isinstance(tag, str) else tag for tag in maybe_tags]
|
|
68
61
|
for package, maybe_tags in imports.items()
|
|
69
62
|
}
|
|
70
63
|
|
|
@@ -114,10 +107,6 @@ class ImportVar:
|
|
|
114
107
|
# The path of the package to import from.
|
|
115
108
|
package_path: str = "/"
|
|
116
109
|
|
|
117
|
-
# whether this import package should be added to transpilePackages in next.config.js
|
|
118
|
-
# https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages
|
|
119
|
-
transpile: bool | None = False
|
|
120
|
-
|
|
121
110
|
@property
|
|
122
111
|
def name(self) -> str:
|
|
123
112
|
"""The name of the import.
|
reflex/utils/misc.py
CHANGED
|
@@ -1,10 +1,53 @@
|
|
|
1
1
|
"""Miscellaneous functions for the experimental package."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
import sys
|
|
6
|
+
import threading
|
|
4
7
|
from collections.abc import Callable
|
|
8
|
+
from pathlib import Path
|
|
5
9
|
from typing import Any
|
|
6
10
|
|
|
7
11
|
|
|
12
|
+
def get_module_path(module_name: str) -> Path | None:
|
|
13
|
+
"""Check if a module exists and return its path.
|
|
14
|
+
|
|
15
|
+
This function searches for a module by navigating through the module hierarchy
|
|
16
|
+
in each path of sys.path, checking for both .py files and packages with __init__.py.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
module_name: The name of the module to search for (e.g., "package.submodule").
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
The path to the module file if found, None otherwise.
|
|
23
|
+
"""
|
|
24
|
+
parts = module_name.split(".")
|
|
25
|
+
|
|
26
|
+
# Check each path in sys.path
|
|
27
|
+
for path in sys.path:
|
|
28
|
+
current_path = Path(path)
|
|
29
|
+
|
|
30
|
+
# Navigate through the module hierarchy
|
|
31
|
+
for i, part in enumerate(parts):
|
|
32
|
+
potential_file = current_path / (part + ".py")
|
|
33
|
+
potential_dir = current_path / part
|
|
34
|
+
|
|
35
|
+
if potential_file.is_file():
|
|
36
|
+
# We encountered a file, but we can't continue deeper
|
|
37
|
+
if i == len(parts) - 1:
|
|
38
|
+
return potential_file
|
|
39
|
+
return None # Can't continue deeper
|
|
40
|
+
if potential_dir.is_dir():
|
|
41
|
+
# It's a package, so we can continue deeper
|
|
42
|
+
current_path = potential_dir
|
|
43
|
+
else:
|
|
44
|
+
break # Path doesn't exist, break out of the loop
|
|
45
|
+
else:
|
|
46
|
+
return current_path / "__init__.py" # Made it through all parts
|
|
47
|
+
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
8
51
|
async def run_in_thread(func: Callable) -> Any:
|
|
9
52
|
"""Run a function in a separate thread.
|
|
10
53
|
|
|
@@ -23,3 +66,27 @@ async def run_in_thread(func: Callable) -> Any:
|
|
|
23
66
|
msg = "func must be a non-async function"
|
|
24
67
|
raise ValueError(msg)
|
|
25
68
|
return await asyncio.get_event_loop().run_in_executor(None, func)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Global lock for thread-safe sys.path manipulation
|
|
72
|
+
_sys_path_lock = threading.RLock()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@contextlib.contextmanager
|
|
76
|
+
def with_cwd_in_syspath():
|
|
77
|
+
"""Temporarily add current working directory to sys.path in a thread-safe manner.
|
|
78
|
+
|
|
79
|
+
This context manager temporarily prepends the current working directory to sys.path,
|
|
80
|
+
ensuring that modules in the current directory can be imported. The original sys.path
|
|
81
|
+
is restored when exiting the context.
|
|
82
|
+
|
|
83
|
+
Yields:
|
|
84
|
+
None
|
|
85
|
+
"""
|
|
86
|
+
with _sys_path_lock:
|
|
87
|
+
orig_sys_path = sys.path.copy()
|
|
88
|
+
sys.path.insert(0, str(Path.cwd()))
|
|
89
|
+
try:
|
|
90
|
+
yield
|
|
91
|
+
finally:
|
|
92
|
+
sys.path[:] = orig_sys_path
|