Run backend from the main thread in dev mode (#1653)
This commit is contained in:
parent
96a6ab95a2
commit
81fd9d1e9c
28
docs/DEBUGGING.md
Normal file
28
docs/DEBUGGING.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Debugging
|
||||||
|
|
||||||
|
It is possible to run Reflex apps in dev mode under a debugger.
|
||||||
|
|
||||||
|
1. Run Reflex as a module: `python -m reflex run --env dev`
|
||||||
|
2. Set current working directory to the dir containing `rxconfig.py`
|
||||||
|
|
||||||
|
## VSCode
|
||||||
|
|
||||||
|
The following launch configuration can be used to interactively debug a Reflex
|
||||||
|
app with breakpoints.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Reflex App",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "reflex",
|
||||||
|
"args": "run --env dev",
|
||||||
|
"justMyCode": true,
|
||||||
|
"cwd": "${fileDirname}/.."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
@ -1,7 +1,7 @@
|
|||||||
"""Reflex CLI to create, run, and deploy apps."""
|
"""Reflex CLI to create, run, and deploy apps."""
|
||||||
|
|
||||||
|
import atexit
|
||||||
import os
|
import os
|
||||||
import signal
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@ -164,16 +164,18 @@ def run(
|
|||||||
telemetry.send(f"run-{env.value}", config.telemetry_enabled)
|
telemetry.send(f"run-{env.value}", config.telemetry_enabled)
|
||||||
|
|
||||||
# Display custom message when there is a keyboard interrupt.
|
# Display custom message when there is a keyboard interrupt.
|
||||||
signal.signal(signal.SIGINT, processes.catch_keyboard_interrupt)
|
atexit.register(processes.atexit_handler)
|
||||||
|
|
||||||
# Run the frontend and backend together.
|
# Run the frontend and backend together.
|
||||||
commands = []
|
commands = []
|
||||||
if frontend:
|
if frontend:
|
||||||
setup_frontend(Path.cwd())
|
setup_frontend(Path.cwd())
|
||||||
commands.append((frontend_cmd, Path.cwd(), frontend_port))
|
commands.append((frontend_cmd, Path.cwd(), frontend_port))
|
||||||
if backend:
|
if backend and env == constants.Env.PROD:
|
||||||
commands.append((backend_cmd, app.__name__, backend_host, backend_port))
|
commands.append((backend_cmd, app.__name__, backend_host, backend_port))
|
||||||
processes.run_concurrently(*commands)
|
with processes.run_concurrently_context(*commands):
|
||||||
|
if env == constants.Env.DEV:
|
||||||
|
backend_cmd(app.__name__, backend_host, int(backend_port))
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
|
@ -7,6 +7,8 @@ import platform
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.config import get_config
|
from reflex.config import get_config
|
||||||
from reflex.utils import console, path_ops, prerequisites, processes
|
from reflex.utils import console, path_ops, prerequisites, processes
|
||||||
@ -95,22 +97,13 @@ def run_backend(
|
|||||||
port: The app port
|
port: The app port
|
||||||
loglevel: The log level.
|
loglevel: The log level.
|
||||||
"""
|
"""
|
||||||
processes.new_process(
|
uvicorn.run(
|
||||||
[
|
app=f"{app_name}:{constants.APP_VAR}.{constants.API_VAR}",
|
||||||
"uvicorn",
|
host=host,
|
||||||
f"{app_name}:{constants.APP_VAR}.{constants.API_VAR}",
|
port=port,
|
||||||
"--host",
|
log_level=loglevel.value,
|
||||||
host,
|
reload=True,
|
||||||
"--port",
|
reload_dirs=[app_name.split(".")[0]],
|
||||||
str(port),
|
|
||||||
"--log-level",
|
|
||||||
loglevel.value,
|
|
||||||
"--reload",
|
|
||||||
"--reload-dir",
|
|
||||||
app_name.split(".")[0],
|
|
||||||
],
|
|
||||||
run=True,
|
|
||||||
show_logs=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import os
|
|||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
from concurrent import futures
|
from concurrent import futures
|
||||||
from typing import Callable, List, Optional, Tuple, Union
|
from typing import Callable, Generator, List, Optional, Tuple, Union
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
import typer
|
import typer
|
||||||
@ -145,13 +145,23 @@ def new_process(args, run: bool = False, show_logs: bool = False, **kwargs):
|
|||||||
return fn(args, **kwargs)
|
return fn(args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def run_concurrently(*fns: Union[Callable, Tuple]):
|
@contextlib.contextmanager
|
||||||
|
def run_concurrently_context(
|
||||||
|
*fns: Union[Callable, Tuple]
|
||||||
|
) -> Generator[list[futures.Future], None, None]:
|
||||||
"""Run functions concurrently in a thread pool.
|
"""Run functions concurrently in a thread pool.
|
||||||
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*fns: The functions to run.
|
*fns: The functions to run.
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
The futures for the functions.
|
||||||
"""
|
"""
|
||||||
|
# If no functions are provided, yield an empty list and return.
|
||||||
|
if not fns:
|
||||||
|
yield []
|
||||||
|
return
|
||||||
|
|
||||||
# Convert the functions to tuples.
|
# Convert the functions to tuples.
|
||||||
fns = [fn if isinstance(fn, tuple) else (fn,) for fn in fns] # type: ignore
|
fns = [fn if isinstance(fn, tuple) else (fn,) for fn in fns] # type: ignore
|
||||||
|
|
||||||
@ -160,11 +170,24 @@ def run_concurrently(*fns: Union[Callable, Tuple]):
|
|||||||
# Submit the tasks.
|
# Submit the tasks.
|
||||||
tasks = [executor.submit(*fn) for fn in fns] # type: ignore
|
tasks = [executor.submit(*fn) for fn in fns] # type: ignore
|
||||||
|
|
||||||
|
# Yield control back to the main thread while tasks are running.
|
||||||
|
yield tasks
|
||||||
|
|
||||||
# Get the results in the order completed to check any exceptions.
|
# Get the results in the order completed to check any exceptions.
|
||||||
for task in futures.as_completed(tasks):
|
for task in futures.as_completed(tasks):
|
||||||
task.result()
|
task.result()
|
||||||
|
|
||||||
|
|
||||||
|
def run_concurrently(*fns: Union[Callable, Tuple]) -> None:
|
||||||
|
"""Run functions concurrently in a thread pool.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
*fns: The functions to run.
|
||||||
|
"""
|
||||||
|
with run_concurrently_context(*fns):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def stream_logs(
|
def stream_logs(
|
||||||
message: str,
|
message: str,
|
||||||
process: subprocess.Popen,
|
process: subprocess.Popen,
|
||||||
@ -247,11 +270,6 @@ def show_progress(message: str, process: subprocess.Popen, checkpoints: List[str
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def catch_keyboard_interrupt(signal, frame):
|
def atexit_handler():
|
||||||
"""Display a custom message with the current time when exiting an app.
|
"""Display a custom message with the current time when exiting an app."""
|
||||||
|
|
||||||
Args:
|
|
||||||
signal: The keyboard interrupt signal.
|
|
||||||
frame: The current stack frame.
|
|
||||||
"""
|
|
||||||
console.log("Reflex app stopped.")
|
console.log("Reflex app stopped.")
|
||||||
|
Loading…
Reference in New Issue
Block a user