Merge 434e794412
into 98f50811f9
This commit is contained in:
commit
ec28d74027
@ -580,6 +580,22 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
"""
|
||||
if not self.api:
|
||||
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
|
||||
|
||||
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.state import reset_disk_state_manager
|
||||
from reflex.utils import console, telemetry
|
||||
from reflex.utils.exec import should_use_granian
|
||||
|
||||
# Disable typer+rich integration for help panels
|
||||
typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
|
||||
@ -192,7 +193,9 @@ def _run(
|
||||
|
||||
if frontend:
|
||||
# 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.
|
||||
prerequisites.check_schema_up_to_date()
|
||||
|
@ -196,22 +196,9 @@ def get_app_module():
|
||||
Returns:
|
||||
The app module for the backend.
|
||||
"""
|
||||
return f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
|
||||
config = get_config()
|
||||
|
||||
|
||||
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}"
|
||||
)
|
||||
return f"{config.module}:{constants.CompileVars.APP}"
|
||||
|
||||
|
||||
def run_backend(
|
||||
@ -229,9 +216,6 @@ def run_backend(
|
||||
frontend_present: Whether the frontend is present.
|
||||
"""
|
||||
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:
|
||||
notify_backend()
|
||||
@ -240,6 +224,9 @@ def run_backend(
|
||||
if should_use_granian():
|
||||
run_granian_backend(host, port, loglevel)
|
||||
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)
|
||||
|
||||
|
||||
@ -313,7 +300,8 @@ def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(
|
||||
app=f"{get_app_module()}.{constants.CompileVars.API}",
|
||||
app=f"{get_app_module()}",
|
||||
factory=True,
|
||||
host=host,
|
||||
port=port,
|
||||
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]
|
||||
|
||||
Granian(
|
||||
target=get_granian_target(),
|
||||
target=get_app_module(),
|
||||
factory=True,
|
||||
address=host,
|
||||
port=port,
|
||||
interface=Interfaces.ASGI,
|
||||
@ -417,6 +406,7 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
|
||||
*("--host", host),
|
||||
*("--port", str(port)),
|
||||
*("--workers", str(_get_backend_workers())),
|
||||
"--factory",
|
||||
app_module,
|
||||
]
|
||||
if constants.IS_WINDOWS
|
||||
@ -482,7 +472,8 @@ def run_granian_backend_prod(host: str, port: int, loglevel: LogLevel):
|
||||
str(port),
|
||||
"--interface",
|
||||
str(Interfaces.ASGI),
|
||||
get_granian_target(),
|
||||
"--factory",
|
||||
get_app_module(),
|
||||
]
|
||||
processes.new_process(
|
||||
command,
|
||||
|
@ -7,6 +7,7 @@ def test_page_decorator():
|
||||
def foo_():
|
||||
return text("foo")
|
||||
|
||||
DECORATED_PAGES.clear()
|
||||
assert len(DECORATED_PAGES) == 0
|
||||
decorated_foo_ = page()(foo_)
|
||||
assert decorated_foo_ == foo_
|
||||
|
Loading…
Reference in New Issue
Block a user