CLI Update + Threading (#1046)

This commit is contained in:
Alek Petuskey 2023-05-18 12:34:37 -07:00 committed by GitHub
parent 6dc2778a15
commit d1703f41b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 59 deletions

View File

@ -378,23 +378,13 @@ class App(Base):
): ):
self.pages[froute(constants.SLUG_404)] = component self.pages[froute(constants.SLUG_404)] = component
def compile(self, force_compile: bool = False): def compile(self):
"""Compile the app and output it to the pages folder. """Compile the app and output it to the pages folder."""
If the config environment is set to production, the app will
not be compiled.
Args:
force_compile: Whether to force the app to compile.
"""
for render, kwargs in DECORATED_ROUTES: for render, kwargs in DECORATED_ROUTES:
self.add_page(render, **kwargs) self.add_page(render, **kwargs)
# Get the env mode. # Get the env mode.
config = get_config() config = get_config()
if config.env != constants.Env.DEV and not force_compile:
print("Skipping compilation in non-dev mode.")
return
# Update models during hot reload. # Update models during hot reload.
if config.db_url is not None: if config.db_url is not None:

View File

@ -2,6 +2,7 @@
import os import os
import platform import platform
import threading
from pathlib import Path from pathlib import Path
import httpx import httpx
@ -125,16 +126,15 @@ def run(
telemetry.send(f"run-{env.value}", get_config().telemetry_enabled) telemetry.send(f"run-{env.value}", get_config().telemetry_enabled)
# Run the frontend and backend. # Run the frontend and backend.
try: # try:
if frontend: if backend:
frontend_cmd(app.app, Path.cwd(), frontend_port) threading.Thread(
if backend: target=backend_cmd, args=(app.__name__, backend_port, loglevel)
backend_cmd(app.__name__, port=int(backend_port), loglevel=loglevel) ).start()
finally: if frontend:
if frontend: threading.Thread(
processes.kill_process_on_port(frontend_port) target=frontend_cmd, args=(app.app, Path.cwd(), frontend_port)
if backend: ).start()
processes.kill_process_on_port(backend_port)
@cli.command() @cli.command()

View File

@ -77,6 +77,7 @@ def export_app(
frontend: bool = True, frontend: bool = True,
zip: bool = False, zip: bool = False,
deploy_url: Optional[str] = None, deploy_url: Optional[str] = None,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
): ):
"""Zip up the app for deployment. """Zip up the app for deployment.
@ -86,10 +87,8 @@ def export_app(
frontend: Whether to zip up the frontend app. frontend: Whether to zip up the frontend app.
zip: Whether to zip the app. zip: Whether to zip the app.
deploy_url: The URL of the deployed app. deploy_url: The URL of the deployed app.
loglevel: The log level to use.
""" """
# Force compile the app.
app.compile(force_compile=True)
# Remove the static folder. # Remove the static folder.
path_ops.rm(constants.WEB_STATIC_DIR) path_ops.rm(constants.WEB_STATIC_DIR)
@ -106,18 +105,17 @@ def export_app(
# Start the progress bar # Start the progress bar
with progress: with progress:
# Run the subprocess command # Run the subprocess command
process = subprocess.Popen( export_process = subprocess.Popen(
[prerequisites.get_package_manager(), "run", "export"], [prerequisites.get_package_manager(), "run", "export"],
cwd=constants.WEB_DIR, cwd=constants.WEB_DIR,
stderr=subprocess.DEVNULL, env=os.environ,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, # Redirect stdout to a pipe stdout=subprocess.PIPE, # Redirect stdout to a pipe
universal_newlines=True, # Set universal_newlines to True for text mode universal_newlines=True, # Set universal_newlines to True for text mode
) )
# Read the output of the subprocess line by line if export_process.stdout:
if process.stdout: for line in iter(export_process.stdout.readline, ""):
for line in iter(process.stdout.readline, ""):
# Update the progress bar based on the output
if "Linting and checking " in line: if "Linting and checking " in line:
progress.update(task, advance=100) progress.update(task, advance=100)
elif "Compiled successfully" in line: elif "Compiled successfully" in line:
@ -127,8 +125,15 @@ def export_app(
elif "automatically rendered as static HTML" in line: elif "automatically rendered as static HTML" in line:
progress.update(task, advance=100) progress.update(task, advance=100)
elif "Export successful" in line: elif "Export successful" in line:
print("DOOE")
progress.update(task, completed=500) progress.update(task, completed=500)
break # Exit the loop if the completion message is found break # Exit the loop if the completion message is found
elif loglevel == constants.LogLevel.DEBUG:
print(line, end="")
# Wait for the subprocess to complete
export_process.wait()
print("Export process completed.")
# Zip up the app. # Zip up the app.
if zip: if zip:

View File

@ -5,11 +5,10 @@ from __future__ import annotations
import os import os
import platform import platform
import subprocess import subprocess
from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import typer
import uvicorn
from rich import print from rich import print
from pynecone import constants from pynecone import constants
@ -32,44 +31,58 @@ def start_watching_assets_folder(root):
asset_watch.start() asset_watch.start()
def run_process_and_launch_url(run_command: list[str], root: Path): def run_process_and_launch_url(
run_command: list[str],
root: Path,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the process and launch the URL. """Run the process and launch the URL.
Args: Args:
run_command: The command to run. run_command: The command to run.
root: root path of the project. root: root path of the project.
loglevel: The log level to use.
""" """
process = subprocess.Popen( process = subprocess.Popen(
run_command, run_command,
cwd=constants.WEB_DIR, cwd=constants.WEB_DIR,
env=os.environ, env=os.environ,
stderr=subprocess.DEVNULL, stderr=subprocess.STDOUT,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
universal_newlines=True, universal_newlines=True,
) )
message_found = False current_time = datetime.now()
if process.stdout: if process.stdout:
for line in process.stdout: for line in process.stdout:
if "ready started server on" in line: if "ready started server on" in line:
url = line.split("url: ")[-1].strip() url = line.split("url: ")[-1].strip()
print(f"App running at: [bold green]{url}") print(f"App running at: [bold green]{url}")
typer.launch(url) if (
message_found = True "Fast Refresh" in line
break and (datetime.now() - current_time).total_seconds() > 1
):
if not message_found and process.stdout: print(
for line in process.stdout: f"[yellow][Updating App][/yellow] Applying changes and refreshing. Time: {current_time}"
print(line, end="") )
current_time = datetime.now()
elif loglevel == constants.LogLevel.DEBUG:
print(line, end="")
def run_frontend(app: App, root: Path, port: str): def run_frontend(
app: App,
root: Path,
port: str,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the frontend. """Run the frontend.
Args: Args:
app: The app. app: The app.
root: root path of the project. root: root path of the project.
port: port of the app. port: port of the app.
loglevel: The log level to use.
""" """
# validate bun version # validate bun version
prerequisites.validate_and_install_bun(initialize=False) prerequisites.validate_and_install_bun(initialize=False)
@ -77,33 +90,36 @@ def run_frontend(app: App, root: Path, port: str):
# Set up the frontend. # Set up the frontend.
setup_frontend(root) setup_frontend(root)
# start watching asset folder # Start watching asset folder.
start_watching_assets_folder(root) start_watching_assets_folder(root)
# Compile the frontend.
app.compile(force_compile=True)
# Run the frontend in development mode. # Run the frontend in development mode.
console.rule("[bold green]App Running") console.rule("[bold green]App Running")
os.environ["PORT"] = get_config().port if port is None else port os.environ["PORT"] = get_config().port if port is None else port
run_process_and_launch_url( run_process_and_launch_url(
[prerequisites.get_package_manager(), "run", "dev"], root [prerequisites.get_package_manager(), "run", "dev"], root, loglevel
) )
def run_frontend_prod(app: App, root: Path, port: str): def run_frontend_prod(
app: App,
root: Path,
port: str,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
):
"""Run the frontend. """Run the frontend.
Args: Args:
app: The app. app: The app.
root: root path of the project. root: root path of the project.
port: port of the app. port: port of the app.
loglevel: The log level to use.
""" """
# Set up the frontend. # Set up the frontend.
setup_frontend(root) setup_frontend(root)
# Export the app. # Export the app.
export_app(app) export_app(app, loglevel=loglevel)
# Set the port. # Set the port.
os.environ["PORT"] = get_config().port if port is None else port os.environ["PORT"] = get_config().port if port is None else port
@ -111,7 +127,7 @@ def run_frontend_prod(app: App, root: Path, port: str):
# Run the frontend in production mode. # Run the frontend in production mode.
console.rule("[bold green]App Running") console.rule("[bold green]App Running")
run_process_and_launch_url( run_process_and_launch_url(
[prerequisites.get_package_manager(), "run", "prod"], root [prerequisites.get_package_manager(), "run", "prod"], root, loglevel
) )
@ -127,13 +143,23 @@ def run_backend(
""" """
setup_backend() setup_backend()
uvicorn.run( cmd = [
"uvicorn",
f"{app_name}:{constants.APP_VAR}.{constants.API_VAR}", f"{app_name}:{constants.APP_VAR}.{constants.API_VAR}",
host=constants.BACKEND_HOST, "--host",
port=port, constants.BACKEND_HOST,
log_level=loglevel, "--port",
reload=True, str(port),
) "--log-level",
loglevel,
"--reload",
]
process = subprocess.Popen(cmd)
try:
process.wait()
except KeyboardInterrupt:
process.terminate()
def run_backend_prod( def run_backend_prod(

View File

@ -15,7 +15,7 @@ while ! nc -z localhost 3000 || ! lsof -i :8000 >/dev/null; do
echo "Error: Server process with PID $pid exited early" echo "Error: Server process with PID $pid exited early"
break break
fi fi
if ((wait_time >= 300)); then if ((wait_time >= 500)); then
echo "Error: Timeout waiting for ports 3000 and 8000 to become available" echo "Error: Timeout waiting for ports 3000 and 8000 to become available"
exit 1 exit 1
fi fi