reflex/pynecone/utils/exec.py
2023-06-20 09:49:09 -07:00

187 lines
4.7 KiB
Python

"""Everything regarding execution of the built app."""
from __future__ import annotations
import os
import platform
import subprocess
from datetime import datetime
from pathlib import Path
from rich import print
from pynecone import constants
from pynecone.config import get_config
from pynecone.utils import console, prerequisites, processes
from pynecone.utils.watch import AssetFolderWatch
def start_watching_assets_folder(root):
"""Start watching assets folder.
Args:
root: root path of the project.
"""
asset_watch = AssetFolderWatch(root)
asset_watch.start()
def run_process_and_launch_url(
run_command: list[str],
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the process and launch the URL.
Args:
run_command: The command to run.
loglevel: The log level to use.
"""
process = subprocess.Popen(
run_command,
cwd=constants.WEB_DIR,
env=os.environ,
stderr=subprocess.STDOUT,
stdout=None if platform.system() == "Windows" else subprocess.PIPE,
universal_newlines=None if platform.system() == "Windows" else True,
)
current_time = datetime.now()
if process.stdout:
for line in process.stdout:
if "ready started server on" in line:
url = line.split("url: ")[-1].strip()
print(f"App running at: [bold green]{url}")
if (
"Fast Refresh" in line
or "compiling..." in line
and (datetime.now() - current_time).total_seconds() > 1
):
current_time = datetime.now()
print(
f"[yellow][Updating App][/yellow] Applying changes and refreshing. Time: {current_time}"
)
elif loglevel == constants.LogLevel.DEBUG:
print(line, end="")
def run_frontend(
root: Path,
port: str,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the frontend.
Args:
root: The root path of the project.
port: The port to run the frontend on.
loglevel: The log level to use.
"""
# Start watching asset folder.
start_watching_assets_folder(root)
# Run the frontend in development mode.
console.rule("[bold green]App Running")
os.environ["PORT"] = get_config().frontend_port if port is None else port
run_process_and_launch_url(
[prerequisites.get_package_manager(), "run", "dev"], loglevel
)
def run_frontend_prod(
root: Path,
port: str,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the frontend.
Args:
root: The root path of the project (to keep same API as run_frontend).
port: The port to run the frontend on.
loglevel: The log level to use.
"""
# Set the port.
os.environ["PORT"] = get_config().frontend_port if port is None else port
# Run the frontend in production mode.
console.rule("[bold green]App Running")
run_process_and_launch_url(
[prerequisites.get_package_manager(), "run", "prod"], loglevel
)
def run_backend(
app_name: str,
host: str,
port: int,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the backend.
Args:
host: The app host
app_name: The app name.
port: The app port
loglevel: The log level.
"""
cmd = [
"uvicorn",
f"{app_name}:{constants.APP_VAR}.{constants.API_VAR}",
"--host",
host,
"--port",
str(port),
"--log-level",
loglevel,
"--reload",
]
process = subprocess.Popen(cmd)
try:
process.wait()
except KeyboardInterrupt:
process.terminate()
def run_backend_prod(
app_name: str,
host: str,
port: int,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the backend.
Args:
host: The app host
app_name: The app name.
port: The app port
loglevel: The log level.
"""
num_workers = processes.get_num_workers()
command = (
[
*constants.RUN_BACKEND_PROD_WINDOWS,
"--host",
host,
"--port",
str(port),
f"{app_name}:{constants.APP_VAR}",
]
if platform.system() == "Windows"
else [
*constants.RUN_BACKEND_PROD,
"--bind",
f"{host}:{port}",
"--threads",
str(num_workers),
f"{app_name}:{constants.APP_VAR}()",
]
)
command += [
"--log-level",
loglevel.value,
"--workers",
str(num_workers),
]
subprocess.run(command)