Merge 434e794412
into 98f50811f9
This commit is contained in:
commit
ec28d74027
@ -580,6 +580,22 @@ class App(MiddlewareMixin, LifespanMixin):
|
|||||||
"""
|
"""
|
||||||
if not self.api:
|
if not self.api:
|
||||||
raise ValueError("The app has not been initialized.")
|
raise ValueError("The app has not been initialized.")
|
||||||
|
|
||||||
|
# For py3.9 compatibility when redis is used, we MUST add any decorator pages
|
||||||
|
# before compiling the app in a thread to avoid event loop error (REF-2172).
|
||||||
|
self._apply_decorated_pages()
|
||||||
|
|
||||||
|
compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit(
|
||||||
|
self._compile
|
||||||
|
)
|
||||||
|
compile_future.add_done_callback(
|
||||||
|
# Force background compile errors to print eagerly
|
||||||
|
lambda f: f.result()
|
||||||
|
)
|
||||||
|
# Wait for the compile to finish in prod mode to ensure all optional endpoints are mounted.
|
||||||
|
if is_prod_mode():
|
||||||
|
compile_future.result()
|
||||||
|
|
||||||
return self.api
|
return self.api
|
||||||
|
|
||||||
def _add_default_endpoints(self):
|
def _add_default_endpoints(self):
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
"""Shims the real reflex app module for running backend server (uvicorn or gunicorn).
|
|
||||||
Only the app attribute is explicitly exposed.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
|
|
||||||
from reflex import constants
|
|
||||||
from reflex.utils.exec import is_prod_mode
|
|
||||||
from reflex.utils.prerequisites import get_and_validate_app
|
|
||||||
|
|
||||||
if constants.CompileVars.APP != "app":
|
|
||||||
raise AssertionError("unexpected variable name for 'app'")
|
|
||||||
|
|
||||||
app, app_module = get_and_validate_app(reload=False)
|
|
||||||
# For py3.9 compatibility when redis is used, we MUST add any decorator pages
|
|
||||||
# before compiling the app in a thread to avoid event loop error (REF-2172).
|
|
||||||
app._apply_decorated_pages()
|
|
||||||
compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile)
|
|
||||||
compile_future.add_done_callback(
|
|
||||||
# Force background compile errors to print eagerly
|
|
||||||
lambda f: f.result()
|
|
||||||
)
|
|
||||||
# Wait for the compile to finish in prod mode to ensure all optional endpoints are mounted.
|
|
||||||
if is_prod_mode():
|
|
||||||
compile_future.result()
|
|
||||||
|
|
||||||
# ensure only "app" is exposed.
|
|
||||||
del app_module
|
|
||||||
del compile_future
|
|
||||||
del get_and_validate_app
|
|
||||||
del is_prod_mode
|
|
||||||
del constants
|
|
||||||
del ThreadPoolExecutor
|
|
@ -15,6 +15,7 @@ from reflex.config import environment, get_config
|
|||||||
from reflex.custom_components.custom_components import custom_components_cli
|
from reflex.custom_components.custom_components import custom_components_cli
|
||||||
from reflex.state import reset_disk_state_manager
|
from reflex.state import reset_disk_state_manager
|
||||||
from reflex.utils import console, telemetry
|
from reflex.utils import console, telemetry
|
||||||
|
from reflex.utils.exec import should_use_granian
|
||||||
|
|
||||||
# Disable typer+rich integration for help panels
|
# Disable typer+rich integration for help panels
|
||||||
typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
|
typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
|
||||||
@ -192,7 +193,9 @@ def _run(
|
|||||||
|
|
||||||
if frontend:
|
if frontend:
|
||||||
# Get the app module.
|
# Get the app module.
|
||||||
prerequisites.get_compiled_app()
|
if not should_use_granian():
|
||||||
|
# Granian fails if the app is already imported.
|
||||||
|
prerequisites.get_compiled_app()
|
||||||
|
|
||||||
# Warn if schema is not up to date.
|
# Warn if schema is not up to date.
|
||||||
prerequisites.check_schema_up_to_date()
|
prerequisites.check_schema_up_to_date()
|
||||||
|
@ -196,22 +196,9 @@ def get_app_module():
|
|||||||
Returns:
|
Returns:
|
||||||
The app module for the backend.
|
The app module for the backend.
|
||||||
"""
|
"""
|
||||||
return f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
config = get_config()
|
||||||
|
|
||||||
|
return f"{config.module}:{constants.CompileVars.APP}"
|
||||||
def get_granian_target():
|
|
||||||
"""Get the Granian target for the backend.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The Granian target for the backend.
|
|
||||||
"""
|
|
||||||
import reflex
|
|
||||||
|
|
||||||
app_module_path = Path(reflex.__file__).parent / "app_module_for_backend.py"
|
|
||||||
|
|
||||||
return (
|
|
||||||
f"{app_module_path!s}:{constants.CompileVars.APP}.{constants.CompileVars.API}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def run_backend(
|
def run_backend(
|
||||||
@ -229,9 +216,6 @@ def run_backend(
|
|||||||
frontend_present: Whether the frontend is present.
|
frontend_present: Whether the frontend is present.
|
||||||
"""
|
"""
|
||||||
web_dir = get_web_dir()
|
web_dir = get_web_dir()
|
||||||
# Create a .nocompile file to skip compile for backend.
|
|
||||||
if web_dir.exists():
|
|
||||||
(web_dir / constants.NOCOMPILE_FILE).touch()
|
|
||||||
|
|
||||||
if not frontend_present:
|
if not frontend_present:
|
||||||
notify_backend()
|
notify_backend()
|
||||||
@ -240,6 +224,9 @@ def run_backend(
|
|||||||
if should_use_granian():
|
if should_use_granian():
|
||||||
run_granian_backend(host, port, loglevel)
|
run_granian_backend(host, port, loglevel)
|
||||||
else:
|
else:
|
||||||
|
# Create a .nocompile file to skip compile for backend.
|
||||||
|
if web_dir.exists():
|
||||||
|
(web_dir / constants.NOCOMPILE_FILE).touch()
|
||||||
run_uvicorn_backend(host, port, loglevel)
|
run_uvicorn_backend(host, port, loglevel)
|
||||||
|
|
||||||
|
|
||||||
@ -313,7 +300,8 @@ def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
app=f"{get_app_module()}.{constants.CompileVars.API}",
|
app=f"{get_app_module()}",
|
||||||
|
factory=True,
|
||||||
host=host,
|
host=host,
|
||||||
port=port,
|
port=port,
|
||||||
log_level=loglevel.value,
|
log_level=loglevel.value,
|
||||||
@ -339,7 +327,8 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
|
|||||||
from granian.log import LogLevels # pyright: ignore [reportMissingImports]
|
from granian.log import LogLevels # pyright: ignore [reportMissingImports]
|
||||||
|
|
||||||
Granian(
|
Granian(
|
||||||
target=get_granian_target(),
|
target=get_app_module(),
|
||||||
|
factory=True,
|
||||||
address=host,
|
address=host,
|
||||||
port=port,
|
port=port,
|
||||||
interface=Interfaces.ASGI,
|
interface=Interfaces.ASGI,
|
||||||
@ -417,6 +406,7 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|||||||
*("--host", host),
|
*("--host", host),
|
||||||
*("--port", str(port)),
|
*("--port", str(port)),
|
||||||
*("--workers", str(_get_backend_workers())),
|
*("--workers", str(_get_backend_workers())),
|
||||||
|
"--factory",
|
||||||
app_module,
|
app_module,
|
||||||
]
|
]
|
||||||
if constants.IS_WINDOWS
|
if constants.IS_WINDOWS
|
||||||
@ -482,7 +472,8 @@ def run_granian_backend_prod(host: str, port: int, loglevel: LogLevel):
|
|||||||
str(port),
|
str(port),
|
||||||
"--interface",
|
"--interface",
|
||||||
str(Interfaces.ASGI),
|
str(Interfaces.ASGI),
|
||||||
get_granian_target(),
|
"--factory",
|
||||||
|
get_app_module(),
|
||||||
]
|
]
|
||||||
processes.new_process(
|
processes.new_process(
|
||||||
command,
|
command,
|
||||||
|
@ -7,6 +7,7 @@ def test_page_decorator():
|
|||||||
def foo_():
|
def foo_():
|
||||||
return text("foo")
|
return text("foo")
|
||||||
|
|
||||||
|
DECORATED_PAGES.clear()
|
||||||
assert len(DECORATED_PAGES) == 0
|
assert len(DECORATED_PAGES) == 0
|
||||||
decorated_foo_ = page()(foo_)
|
decorated_foo_ = page()(foo_)
|
||||||
assert decorated_foo_ == foo_
|
assert decorated_foo_ == foo_
|
||||||
|
Loading…
Reference in New Issue
Block a user