reflex 0.7.3a2__py3-none-any.whl → 0.7.4a1__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/app.py +17 -1
- reflex/compiler/compiler.py +63 -8
- reflex/components/core/client_side_routing.py +3 -3
- reflex/components/core/cond.py +20 -12
- reflex/components/core/upload.py +1 -1
- reflex/components/dynamic.py +2 -4
- reflex/components/lucide/icon.py +20 -27
- reflex/components/plotly/plotly.py +9 -9
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/sonner/toast.py +1 -1
- reflex/config.py +19 -22
- reflex/constants/__init__.py +1 -2
- reflex/constants/base.py +3 -0
- reflex/constants/installer.py +8 -105
- reflex/custom_components/custom_components.py +8 -3
- reflex/reflex.py +18 -4
- reflex/state.py +9 -1
- reflex/testing.py +7 -1
- reflex/utils/build.py +3 -4
- reflex/utils/exec.py +146 -71
- reflex/utils/path_ops.py +15 -25
- reflex/utils/prerequisites.py +133 -182
- reflex/utils/processes.py +24 -21
- reflex/utils/registry.py +5 -5
- reflex/vars/base.py +2 -3
- reflex/vars/sequence.py +84 -0
- {reflex-0.7.3a2.dist-info → reflex-0.7.4a1.dist-info}/METADATA +3 -3
- {reflex-0.7.3a2.dist-info → reflex-0.7.4a1.dist-info}/RECORD +31 -32
- reflex/app_module_for_backend.py +0 -33
- {reflex-0.7.3a2.dist-info → reflex-0.7.4a1.dist-info}/WHEEL +0 -0
- {reflex-0.7.3a2.dist-info → reflex-0.7.4a1.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.3a2.dist-info → reflex-0.7.4a1.dist-info}/licenses/LICENSE +0 -0
reflex/constants/installer.py
CHANGED
|
@@ -1,46 +1,22 @@
|
|
|
1
|
-
"""File for constants related to the installation process. (Bun/
|
|
1
|
+
"""File for constants related to the installation process. (Bun/Node)."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import platform
|
|
6
|
-
from pathlib import Path
|
|
7
5
|
from types import SimpleNamespace
|
|
8
6
|
|
|
9
7
|
from .base import IS_WINDOWS
|
|
10
8
|
from .utils import classproperty
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
def get_fnm_name() -> str | None:
|
|
14
|
-
"""Get the appropriate fnm executable name based on the current platform.
|
|
15
|
-
|
|
16
|
-
Returns:
|
|
17
|
-
The fnm executable name for the current platform.
|
|
18
|
-
"""
|
|
19
|
-
platform_os = platform.system()
|
|
20
|
-
|
|
21
|
-
if platform_os == "Windows":
|
|
22
|
-
return "fnm-windows"
|
|
23
|
-
elif platform_os == "Darwin":
|
|
24
|
-
return "fnm-macos"
|
|
25
|
-
elif platform_os == "Linux":
|
|
26
|
-
machine = platform.machine()
|
|
27
|
-
if machine == "arm" or machine.startswith("armv7"):
|
|
28
|
-
return "fnm-arm32"
|
|
29
|
-
elif machine.startswith("aarch") or machine.startswith("armv8"):
|
|
30
|
-
return "fnm-arm64"
|
|
31
|
-
return "fnm-linux"
|
|
32
|
-
return None
|
|
33
|
-
|
|
34
|
-
|
|
35
11
|
# Bun config.
|
|
36
12
|
class Bun(SimpleNamespace):
|
|
37
13
|
"""Bun constants."""
|
|
38
14
|
|
|
39
15
|
# The Bun version.
|
|
40
|
-
VERSION = "1.2.
|
|
16
|
+
VERSION = "1.2.4"
|
|
41
17
|
|
|
42
18
|
# Min Bun Version
|
|
43
|
-
MIN_VERSION = "1.
|
|
19
|
+
MIN_VERSION = "1.2.4"
|
|
44
20
|
|
|
45
21
|
# URL to bun install script.
|
|
46
22
|
INSTALL_URL = "https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/bun_install.sh"
|
|
@@ -81,43 +57,6 @@ registry = "{registry}"
|
|
|
81
57
|
"""
|
|
82
58
|
|
|
83
59
|
|
|
84
|
-
# FNM config.
|
|
85
|
-
class Fnm(SimpleNamespace):
|
|
86
|
-
"""FNM constants."""
|
|
87
|
-
|
|
88
|
-
# The FNM version.
|
|
89
|
-
VERSION = "1.35.1"
|
|
90
|
-
|
|
91
|
-
FILENAME = get_fnm_name()
|
|
92
|
-
|
|
93
|
-
# The URL to the fnm release binary
|
|
94
|
-
INSTALL_URL = (
|
|
95
|
-
f"https://github.com/Schniz/fnm/releases/download/v{VERSION}/{FILENAME}.zip"
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
@classproperty
|
|
99
|
-
@classmethod
|
|
100
|
-
def DIR(cls) -> Path:
|
|
101
|
-
"""The directory to store fnm.
|
|
102
|
-
|
|
103
|
-
Returns:
|
|
104
|
-
The directory to store fnm.
|
|
105
|
-
"""
|
|
106
|
-
from reflex.config import environment
|
|
107
|
-
|
|
108
|
-
return environment.REFLEX_DIR.get() / "fnm"
|
|
109
|
-
|
|
110
|
-
@classproperty
|
|
111
|
-
@classmethod
|
|
112
|
-
def EXE(cls):
|
|
113
|
-
"""The fnm executable binary.
|
|
114
|
-
|
|
115
|
-
Returns:
|
|
116
|
-
The fnm executable binary.
|
|
117
|
-
"""
|
|
118
|
-
return cls.DIR / ("fnm.exe" if IS_WINDOWS else "fnm")
|
|
119
|
-
|
|
120
|
-
|
|
121
60
|
# Node / NPM config
|
|
122
61
|
class Node(SimpleNamespace):
|
|
123
62
|
"""Node/ NPM constants."""
|
|
@@ -127,42 +66,6 @@ class Node(SimpleNamespace):
|
|
|
127
66
|
# The minimum required node version.
|
|
128
67
|
MIN_VERSION = "18.18.0"
|
|
129
68
|
|
|
130
|
-
@classproperty
|
|
131
|
-
@classmethod
|
|
132
|
-
def BIN_PATH(cls):
|
|
133
|
-
"""The node bin path.
|
|
134
|
-
|
|
135
|
-
Returns:
|
|
136
|
-
The node bin path.
|
|
137
|
-
"""
|
|
138
|
-
return (
|
|
139
|
-
Fnm.DIR
|
|
140
|
-
/ "node-versions"
|
|
141
|
-
/ f"v{cls.VERSION}"
|
|
142
|
-
/ "installation"
|
|
143
|
-
/ ("bin" if not IS_WINDOWS else "")
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
@classproperty
|
|
147
|
-
@classmethod
|
|
148
|
-
def PATH(cls):
|
|
149
|
-
"""The default path where node is installed.
|
|
150
|
-
|
|
151
|
-
Returns:
|
|
152
|
-
The default path where node is installed.
|
|
153
|
-
"""
|
|
154
|
-
return cls.BIN_PATH / ("node.exe" if IS_WINDOWS else "node")
|
|
155
|
-
|
|
156
|
-
@classproperty
|
|
157
|
-
@classmethod
|
|
158
|
-
def NPM_PATH(cls):
|
|
159
|
-
"""The default path where npm is installed.
|
|
160
|
-
|
|
161
|
-
Returns:
|
|
162
|
-
The default path where npm is installed.
|
|
163
|
-
"""
|
|
164
|
-
return cls.BIN_PATH / "npm"
|
|
165
|
-
|
|
166
69
|
|
|
167
70
|
class PackageJson(SimpleNamespace):
|
|
168
71
|
"""Constants used to build the package.json file."""
|
|
@@ -179,11 +82,11 @@ class PackageJson(SimpleNamespace):
|
|
|
179
82
|
|
|
180
83
|
DEPENDENCIES = {
|
|
181
84
|
"@emotion/react": "11.14.0",
|
|
182
|
-
"axios": "1.
|
|
85
|
+
"axios": "1.8.3",
|
|
183
86
|
"json5": "2.2.3",
|
|
184
|
-
"next": "15.
|
|
87
|
+
"next": "15.0.4",
|
|
185
88
|
"next-sitemap": "4.2.3",
|
|
186
|
-
"next-themes": "0.4.
|
|
89
|
+
"next-themes": "0.4.6",
|
|
187
90
|
"react": "19.0.0",
|
|
188
91
|
"react-dom": "19.0.0",
|
|
189
92
|
"react-focus-lock": "2.13.6",
|
|
@@ -191,8 +94,8 @@ class PackageJson(SimpleNamespace):
|
|
|
191
94
|
"universal-cookie": "7.2.2",
|
|
192
95
|
}
|
|
193
96
|
DEV_DEPENDENCIES = {
|
|
194
|
-
"autoprefixer": "10.4.
|
|
195
|
-
"postcss": "8.5.
|
|
97
|
+
"autoprefixer": "10.4.21",
|
|
98
|
+
"postcss": "8.5.3",
|
|
196
99
|
"postcss-import": "16.1.0",
|
|
197
100
|
}
|
|
198
101
|
OVERRIDES = {
|
|
@@ -826,12 +826,19 @@ def _collect_details_for_gallery():
|
|
|
826
826
|
Raises:
|
|
827
827
|
Exit: If pyproject.toml file is ill-formed or the request to the backend services fails.
|
|
828
828
|
"""
|
|
829
|
+
import reflex_cli.constants
|
|
829
830
|
from reflex_cli.utils import hosting
|
|
830
831
|
|
|
831
832
|
console.rule("[bold]Authentication with Reflex Services")
|
|
832
833
|
console.print("First let's log in to Reflex backend services.")
|
|
833
834
|
access_token, _ = hosting.authenticated_token()
|
|
834
835
|
|
|
836
|
+
if not access_token:
|
|
837
|
+
console.error(
|
|
838
|
+
"Unable to authenticate with Reflex backend services. Make sure you are logged in."
|
|
839
|
+
)
|
|
840
|
+
raise typer.Exit(code=1)
|
|
841
|
+
|
|
835
842
|
console.rule("[bold]Custom Component Information")
|
|
836
843
|
params = {}
|
|
837
844
|
package_name = None
|
|
@@ -845,10 +852,8 @@ def _collect_details_for_gallery():
|
|
|
845
852
|
console.print(f"[ Custom component package name ] : {package_name}")
|
|
846
853
|
params["package_name"] = package_name
|
|
847
854
|
|
|
848
|
-
config = get_config()
|
|
849
|
-
|
|
850
855
|
post_custom_components_gallery_endpoint = (
|
|
851
|
-
f"{
|
|
856
|
+
f"{reflex_cli.constants.Hosting.HOSTING_SERVICE}/custom-components/gallery"
|
|
852
857
|
)
|
|
853
858
|
|
|
854
859
|
# Check the backend services if the user is allowed to update information of this package is already shared.
|
reflex/reflex.py
CHANGED
|
@@ -7,13 +7,14 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
import typer.core
|
|
10
|
-
from reflex_cli.v2.deployments import
|
|
10
|
+
from reflex_cli.v2.deployments import hosting_cli
|
|
11
11
|
|
|
12
12
|
from reflex import constants
|
|
13
13
|
from reflex.config import environment, get_config
|
|
14
14
|
from reflex.custom_components.custom_components import custom_components_cli
|
|
15
15
|
from reflex.state import reset_disk_state_manager
|
|
16
16
|
from reflex.utils import console, redir, telemetry
|
|
17
|
+
from reflex.utils.exec import should_use_granian
|
|
17
18
|
|
|
18
19
|
# Disable typer+rich integration for help panels
|
|
19
20
|
typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
|
|
@@ -203,9 +204,19 @@ def _run(
|
|
|
203
204
|
|
|
204
205
|
prerequisites.check_latest_package_version(constants.Reflex.MODULE_NAME)
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
# Get the app module.
|
|
208
|
+
app_task = prerequisites.compile_app if frontend else prerequisites.validate_app
|
|
209
|
+
|
|
210
|
+
# Granian fails if the app is already imported.
|
|
211
|
+
if should_use_granian():
|
|
212
|
+
import concurrent.futures
|
|
213
|
+
|
|
214
|
+
compile_future = concurrent.futures.ProcessPoolExecutor(max_workers=1).submit(
|
|
215
|
+
app_task
|
|
216
|
+
)
|
|
217
|
+
compile_future.result()
|
|
218
|
+
else:
|
|
219
|
+
app_task()
|
|
209
220
|
|
|
210
221
|
# Warn if schema is not up to date.
|
|
211
222
|
prerequisites.check_schema_up_to_date()
|
|
@@ -386,6 +397,7 @@ def export(
|
|
|
386
397
|
def login(loglevel: constants.LogLevel | None = typer.Option(None)):
|
|
387
398
|
"""Authenticate with experimental Reflex hosting service."""
|
|
388
399
|
from reflex_cli.v2 import cli as hosting_cli
|
|
400
|
+
from reflex_cli.v2.deployments import check_version
|
|
389
401
|
|
|
390
402
|
loglevel = loglevel or get_config().loglevel
|
|
391
403
|
|
|
@@ -407,6 +419,7 @@ def logout(
|
|
|
407
419
|
):
|
|
408
420
|
"""Log out of access to Reflex hosting service."""
|
|
409
421
|
from reflex_cli.v2.cli import logout
|
|
422
|
+
from reflex_cli.v2.deployments import check_version
|
|
410
423
|
|
|
411
424
|
check_version()
|
|
412
425
|
|
|
@@ -567,6 +580,7 @@ def deploy(
|
|
|
567
580
|
from reflex_cli.constants.base import LogLevel as HostingLogLevel
|
|
568
581
|
from reflex_cli.utils import dependency
|
|
569
582
|
from reflex_cli.v2 import cli as hosting_cli
|
|
583
|
+
from reflex_cli.v2.deployments import check_version
|
|
570
584
|
|
|
571
585
|
from reflex.utils import export as export_utils
|
|
572
586
|
from reflex.utils import prerequisites
|
reflex/state.py
CHANGED
|
@@ -905,7 +905,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
905
905
|
]
|
|
906
906
|
if len(parent_states) >= 2:
|
|
907
907
|
raise ValueError(f"Only one parent state is allowed {parent_states}.")
|
|
908
|
-
|
|
908
|
+
# The first non-mixin state in the mro is our parent.
|
|
909
|
+
for base in cls.mro()[1:]:
|
|
910
|
+
if base._mixin or not issubclass(base, BaseState):
|
|
911
|
+
continue
|
|
912
|
+
if base is BaseState:
|
|
913
|
+
break
|
|
914
|
+
return base
|
|
915
|
+
return None # No known parent
|
|
909
916
|
|
|
910
917
|
@classmethod
|
|
911
918
|
@functools.lru_cache()
|
|
@@ -1664,6 +1671,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1664
1671
|
|
|
1665
1672
|
raise TypeError(
|
|
1666
1673
|
f"Your handler {handler.fn.__qualname__} must only return/yield: None, Events or other EventHandlers referenced by their class (i.e. using `type(self)` or other class references)."
|
|
1674
|
+
f" Returned events of types {', '.join(map(str, map(type, events)))!s}."
|
|
1667
1675
|
)
|
|
1668
1676
|
|
|
1669
1677
|
async def _as_state_update(
|
reflex/testing.py
CHANGED
|
@@ -371,7 +371,13 @@ class AppHarness:
|
|
|
371
371
|
|
|
372
372
|
# Start the frontend.
|
|
373
373
|
self.frontend_process = reflex.utils.processes.new_process(
|
|
374
|
-
[
|
|
374
|
+
[
|
|
375
|
+
*reflex.utils.prerequisites.get_js_package_executor(raise_on_none=True)[
|
|
376
|
+
0
|
|
377
|
+
],
|
|
378
|
+
"run",
|
|
379
|
+
"dev",
|
|
380
|
+
],
|
|
375
381
|
cwd=self.app_path / reflex.utils.prerequisites.get_web_dir(),
|
|
376
382
|
env={"PORT": "0"},
|
|
377
383
|
**FRONTEND_POPEN_ARGS,
|
reflex/utils/build.py
CHANGED
|
@@ -212,7 +212,7 @@ def build(
|
|
|
212
212
|
|
|
213
213
|
# Start the subprocess with the progress bar.
|
|
214
214
|
process = processes.new_process(
|
|
215
|
-
[prerequisites.
|
|
215
|
+
[*prerequisites.get_js_package_executor(raise_on_none=True)[0], "run", command],
|
|
216
216
|
cwd=wdir,
|
|
217
217
|
shell=constants.IS_WINDOWS,
|
|
218
218
|
)
|
|
@@ -231,11 +231,10 @@ def setup_frontend(
|
|
|
231
231
|
"""
|
|
232
232
|
# Create the assets dir if it doesn't exist.
|
|
233
233
|
path_ops.mkdir(constants.Dirs.APP_ASSETS)
|
|
234
|
-
|
|
235
|
-
# Copy asset files to public folder.
|
|
236
234
|
path_ops.cp(
|
|
237
235
|
src=str(root / constants.Dirs.APP_ASSETS),
|
|
238
236
|
dest=str(root / prerequisites.get_web_dir() / constants.Dirs.PUBLIC),
|
|
237
|
+
ignore=tuple(f"*.{ext}" for ext in constants.Reflex.STYLESHEETS_SUPPORTED),
|
|
239
238
|
)
|
|
240
239
|
|
|
241
240
|
# Set the environment variables in client (env.json).
|
|
@@ -248,7 +247,7 @@ def setup_frontend(
|
|
|
248
247
|
if disable_telemetry:
|
|
249
248
|
processes.new_process(
|
|
250
249
|
[
|
|
251
|
-
prerequisites.
|
|
250
|
+
*prerequisites.get_js_package_executor(raise_on_none=True)[0],
|
|
252
251
|
"run",
|
|
253
252
|
"next",
|
|
254
253
|
"telemetry",
|
reflex/utils/exec.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import hashlib
|
|
6
|
+
import importlib.util
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
8
9
|
import platform
|
|
@@ -19,6 +20,7 @@ from reflex import constants
|
|
|
19
20
|
from reflex.config import environment, get_config
|
|
20
21
|
from reflex.constants.base import LogLevel
|
|
21
22
|
from reflex.utils import console, path_ops
|
|
23
|
+
from reflex.utils.decorator import once
|
|
22
24
|
from reflex.utils.prerequisites import get_web_dir
|
|
23
25
|
|
|
24
26
|
# For uvicorn windows bug fix (#2335)
|
|
@@ -154,7 +156,11 @@ def run_frontend(root: Path, port: str, backend_present: bool = True):
|
|
|
154
156
|
console.rule("[bold green]App Running")
|
|
155
157
|
os.environ["PORT"] = str(get_config().frontend_port if port is None else port)
|
|
156
158
|
run_process_and_launch_url(
|
|
157
|
-
[
|
|
159
|
+
[
|
|
160
|
+
*prerequisites.get_js_package_executor(raise_on_none=True)[0],
|
|
161
|
+
"run",
|
|
162
|
+
"dev",
|
|
163
|
+
],
|
|
158
164
|
backend_present,
|
|
159
165
|
)
|
|
160
166
|
|
|
@@ -176,18 +182,33 @@ def run_frontend_prod(root: Path, port: str, backend_present: bool = True):
|
|
|
176
182
|
# Run the frontend in production mode.
|
|
177
183
|
console.rule("[bold green]App Running")
|
|
178
184
|
run_process_and_launch_url(
|
|
179
|
-
[prerequisites.
|
|
185
|
+
[*prerequisites.get_js_package_executor(raise_on_none=True)[0], "run", "prod"],
|
|
180
186
|
backend_present,
|
|
181
187
|
)
|
|
182
188
|
|
|
183
189
|
|
|
190
|
+
@once
|
|
191
|
+
def _warn_user_about_uvicorn():
|
|
192
|
+
console.warn(
|
|
193
|
+
"Using Uvicorn for backend as it is installed. This behavior will change in 0.8.0 to use Granian by default."
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
184
197
|
def should_use_granian():
|
|
185
198
|
"""Whether to use Granian for backend.
|
|
186
199
|
|
|
187
200
|
Returns:
|
|
188
201
|
True if Granian should be used.
|
|
189
202
|
"""
|
|
190
|
-
|
|
203
|
+
if environment.REFLEX_USE_GRANIAN.get():
|
|
204
|
+
return True
|
|
205
|
+
if (
|
|
206
|
+
importlib.util.find_spec("uvicorn") is None
|
|
207
|
+
or importlib.util.find_spec("gunicorn") is None
|
|
208
|
+
):
|
|
209
|
+
return True
|
|
210
|
+
_warn_user_about_uvicorn()
|
|
211
|
+
return False
|
|
191
212
|
|
|
192
213
|
|
|
193
214
|
def get_app_module():
|
|
@@ -196,22 +217,9 @@ def get_app_module():
|
|
|
196
217
|
Returns:
|
|
197
218
|
The app module for the backend.
|
|
198
219
|
"""
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def get_granian_target():
|
|
203
|
-
"""Get the Granian target for the backend.
|
|
204
|
-
|
|
205
|
-
Returns:
|
|
206
|
-
The Granian target for the backend.
|
|
207
|
-
"""
|
|
208
|
-
import reflex
|
|
220
|
+
config = get_config()
|
|
209
221
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return (
|
|
213
|
-
f"{app_module_path!s}:{constants.CompileVars.APP}.{constants.CompileVars.API}"
|
|
214
|
-
)
|
|
222
|
+
return f"{config.module}:{constants.CompileVars.APP}"
|
|
215
223
|
|
|
216
224
|
|
|
217
225
|
def run_backend(
|
|
@@ -313,7 +321,8 @@ def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
|
|
|
313
321
|
import uvicorn
|
|
314
322
|
|
|
315
323
|
uvicorn.run(
|
|
316
|
-
app=f"{get_app_module()}
|
|
324
|
+
app=f"{get_app_module()}",
|
|
325
|
+
factory=True,
|
|
317
326
|
host=host,
|
|
318
327
|
port=port,
|
|
319
328
|
log_level=loglevel.value,
|
|
@@ -331,31 +340,37 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
|
|
|
331
340
|
loglevel: The log level.
|
|
332
341
|
"""
|
|
333
342
|
console.debug("Using Granian for backend")
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
console.error(
|
|
350
|
-
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian[reload]>=1.6.0"`)'
|
|
351
|
-
)
|
|
352
|
-
os._exit(1)
|
|
343
|
+
|
|
344
|
+
from granian.constants import Interfaces
|
|
345
|
+
from granian.log import LogLevels
|
|
346
|
+
from granian.server import MPServer as Granian
|
|
347
|
+
|
|
348
|
+
Granian(
|
|
349
|
+
target=get_app_module(),
|
|
350
|
+
factory=True,
|
|
351
|
+
address=host,
|
|
352
|
+
port=port,
|
|
353
|
+
interface=Interfaces.ASGI,
|
|
354
|
+
log_level=LogLevels(loglevel.value),
|
|
355
|
+
reload=True,
|
|
356
|
+
reload_paths=get_reload_paths(),
|
|
357
|
+
).serve()
|
|
353
358
|
|
|
354
359
|
|
|
360
|
+
@once
|
|
355
361
|
def _get_backend_workers():
|
|
356
362
|
from reflex.utils import processes
|
|
357
363
|
|
|
358
364
|
config = get_config()
|
|
365
|
+
|
|
366
|
+
if config.gunicorn_workers is not None:
|
|
367
|
+
console.deprecate(
|
|
368
|
+
"config.gunicorn_workers",
|
|
369
|
+
reason="If you're using Granian, use GRANIAN_WORKERS instead.",
|
|
370
|
+
deprecation_version="0.7.4",
|
|
371
|
+
removal_version="0.8.0",
|
|
372
|
+
)
|
|
373
|
+
|
|
359
374
|
return (
|
|
360
375
|
processes.get_num_workers()
|
|
361
376
|
if not config.gunicorn_workers
|
|
@@ -363,6 +378,51 @@ def _get_backend_workers():
|
|
|
363
378
|
)
|
|
364
379
|
|
|
365
380
|
|
|
381
|
+
@once
|
|
382
|
+
def _get_backend_timeout():
|
|
383
|
+
config = get_config()
|
|
384
|
+
|
|
385
|
+
if config.timeout is not None:
|
|
386
|
+
console.deprecate(
|
|
387
|
+
"config.timeout",
|
|
388
|
+
reason="If you're using Granian, use GRANIAN_WORKERS_LIFETIME instead.",
|
|
389
|
+
deprecation_version="0.7.4",
|
|
390
|
+
removal_version="0.8.0",
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
return config.timeout
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
@once
|
|
397
|
+
def _get_backend_max_requests():
|
|
398
|
+
config = get_config()
|
|
399
|
+
|
|
400
|
+
if config.gunicorn_max_requests is not None:
|
|
401
|
+
console.deprecate(
|
|
402
|
+
"config.gunicorn_max_requests",
|
|
403
|
+
reason="",
|
|
404
|
+
deprecation_version="0.7.4",
|
|
405
|
+
removal_version="0.8.0",
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
return config.gunicorn_max_requests
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
@once
|
|
412
|
+
def _get_backend_max_requests_jitter():
|
|
413
|
+
config = get_config()
|
|
414
|
+
|
|
415
|
+
if config.gunicorn_max_requests_jitter is not None:
|
|
416
|
+
console.deprecate(
|
|
417
|
+
"config.gunicorn_max_requests_jitter",
|
|
418
|
+
reason="",
|
|
419
|
+
deprecation_version="0.7.4",
|
|
420
|
+
removal_version="0.8.0",
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
return config.gunicorn_max_requests_jitter
|
|
424
|
+
|
|
425
|
+
|
|
366
426
|
def run_backend_prod(
|
|
367
427
|
host: str,
|
|
368
428
|
port: int,
|
|
@@ -404,17 +464,25 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|
|
404
464
|
[
|
|
405
465
|
"uvicorn",
|
|
406
466
|
*(
|
|
407
|
-
|
|
467
|
+
(
|
|
408
468
|
"--limit-max-requests",
|
|
409
|
-
str(
|
|
410
|
-
|
|
411
|
-
if
|
|
412
|
-
|
|
469
|
+
str(max_requessts),
|
|
470
|
+
)
|
|
471
|
+
if (
|
|
472
|
+
(max_requessts := _get_backend_max_requests()) is not None
|
|
473
|
+
and max_requessts > 0
|
|
474
|
+
)
|
|
475
|
+
else ()
|
|
476
|
+
),
|
|
477
|
+
*(
|
|
478
|
+
("--timeout-keep-alive", str(timeout))
|
|
479
|
+
if (timeout := _get_backend_timeout()) is not None
|
|
480
|
+
else ()
|
|
413
481
|
),
|
|
414
|
-
*("--timeout-keep-alive", str(config.timeout)),
|
|
415
482
|
*("--host", host),
|
|
416
483
|
*("--port", str(port)),
|
|
417
484
|
*("--workers", str(_get_backend_workers())),
|
|
485
|
+
"--factory",
|
|
418
486
|
app_module,
|
|
419
487
|
]
|
|
420
488
|
if constants.IS_WINDOWS
|
|
@@ -422,17 +490,34 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|
|
422
490
|
"gunicorn",
|
|
423
491
|
*("--worker-class", config.gunicorn_worker_class),
|
|
424
492
|
*(
|
|
425
|
-
|
|
493
|
+
(
|
|
426
494
|
"--max-requests",
|
|
427
|
-
str(
|
|
495
|
+
str(max_requessts),
|
|
496
|
+
)
|
|
497
|
+
if (
|
|
498
|
+
(max_requessts := _get_backend_max_requests()) is not None
|
|
499
|
+
and max_requessts > 0
|
|
500
|
+
)
|
|
501
|
+
else ()
|
|
502
|
+
),
|
|
503
|
+
*(
|
|
504
|
+
(
|
|
428
505
|
"--max-requests-jitter",
|
|
429
|
-
str(
|
|
430
|
-
|
|
431
|
-
if
|
|
432
|
-
|
|
506
|
+
str(max_requessts_jitter),
|
|
507
|
+
)
|
|
508
|
+
if (
|
|
509
|
+
(max_requessts_jitter := _get_backend_max_requests_jitter())
|
|
510
|
+
is not None
|
|
511
|
+
and max_requessts_jitter > 0
|
|
512
|
+
)
|
|
513
|
+
else ()
|
|
433
514
|
),
|
|
434
515
|
"--preload",
|
|
435
|
-
*(
|
|
516
|
+
*(
|
|
517
|
+
("--timeout", str(timeout))
|
|
518
|
+
if (timeout := _get_backend_timeout()) is not None
|
|
519
|
+
else ()
|
|
520
|
+
),
|
|
436
521
|
*("--bind", f"{host}:{port}"),
|
|
437
522
|
*("--threads", str(_get_backend_workers())),
|
|
438
523
|
f"{app_module}()",
|
|
@@ -468,17 +553,12 @@ def run_granian_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|
|
468
553
|
|
|
469
554
|
command = [
|
|
470
555
|
"granian",
|
|
471
|
-
"--workers",
|
|
472
|
-
|
|
473
|
-
"--
|
|
474
|
-
"
|
|
475
|
-
"--
|
|
476
|
-
|
|
477
|
-
"--port",
|
|
478
|
-
str(port),
|
|
479
|
-
"--interface",
|
|
480
|
-
str(Interfaces.ASGI),
|
|
481
|
-
get_granian_target(),
|
|
556
|
+
*("--workers", str(_get_backend_workers())),
|
|
557
|
+
*("--log-level", "critical"),
|
|
558
|
+
*("--host", host),
|
|
559
|
+
*("--port", str(port)),
|
|
560
|
+
*("--interface", str(Interfaces.ASGI)),
|
|
561
|
+
*("--factory", get_app_module()),
|
|
482
562
|
]
|
|
483
563
|
processes.new_process(
|
|
484
564
|
command,
|
|
@@ -518,13 +598,8 @@ def output_system_info():
|
|
|
518
598
|
|
|
519
599
|
system = platform.system()
|
|
520
600
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
dependencies.extend(
|
|
524
|
-
[
|
|
525
|
-
fnm_info,
|
|
526
|
-
f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.Bun.VERSION}) (PATH: {path_ops.get_bun_path()})]",
|
|
527
|
-
],
|
|
601
|
+
dependencies.append(
|
|
602
|
+
f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.Bun.VERSION}) (PATH: {path_ops.get_bun_path()})]"
|
|
528
603
|
)
|
|
529
604
|
|
|
530
605
|
if system == "Linux":
|
|
@@ -540,10 +615,10 @@ def output_system_info():
|
|
|
540
615
|
console.debug(f"{dep}")
|
|
541
616
|
|
|
542
617
|
console.debug(
|
|
543
|
-
f"Using package installer at: {prerequisites.
|
|
618
|
+
f"Using package installer at: {prerequisites.get_nodejs_compatible_package_managers(raise_on_none=False)}"
|
|
544
619
|
)
|
|
545
620
|
console.debug(
|
|
546
|
-
f"Using package executer at: {prerequisites.
|
|
621
|
+
f"Using package executer at: {prerequisites.get_js_package_executor(raise_on_none=False)}"
|
|
547
622
|
)
|
|
548
623
|
if system != "Windows":
|
|
549
624
|
console.debug(f"Unzip path: {path_ops.which('unzip')}")
|