diff --git a/pynecone/pc.py b/pynecone/pc.py index cba0c8293..58d6268a2 100644 --- a/pynecone/pc.py +++ b/pynecone/pc.py @@ -8,6 +8,8 @@ import typer from pynecone import constants, utils +from rich.prompt import Prompt + # Create the app. cli = typer.Typer() @@ -60,6 +62,16 @@ def run( port: str = typer.Option(None, help="Specify a different port."), ): """Run the app in the current directory.""" + frontend_port = utils.get_config().port if port is None else port + backend_port = utils.get_api_port() + + # If something is running on the ports, ask the user if they want to kill it. + if utils.is_process_on_port(frontend_port): + utils.terminate_port(frontend_port, "frontend") + + if utils.is_process_on_port(backend_port): + utils.terminate_port(backend_port, "backend") + # Check that the app is initialized. if frontend and not utils.is_initialized(): utils.console.print( @@ -93,8 +105,8 @@ def run( if backend: backend_cmd(app.__name__, loglevel=loglevel) finally: - utils.kill_process_on_port(os.environ["PORT"]) - utils.kill_process_on_port(utils.get_api_port()) + utils.kill_process_on_port(frontend_port) + utils.kill_process_on_port(backend_port) @cli.command() diff --git a/pynecone/utils.py b/pynecone/utils.py index 940a4a7fa..c83d3c612 100644 --- a/pynecone/utils.py +++ b/pynecone/utils.py @@ -37,6 +37,7 @@ import uvicorn from plotly.io import to_json from redis import Redis from rich.console import Console +from rich.prompt import Prompt from pynecone import constants from pynecone.base import Base @@ -582,20 +583,63 @@ def get_api_port() -> int: return port +def get_process_on_port(port) -> Optional[psutil.Process]: + """Get the process on the given port. + + Args: + port: The port. + + Returns: + The process on the given port. + """ + for proc in psutil.process_iter(["pid", "name", "cmdline"]): + try: + for conns in proc.connections(kind="inet"): + if conns.laddr.port == int(port): + return proc + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + pass + return None + + +def is_process_on_port(port) -> bool: + """Check if a process is running on the given port. + + Args: + port: The port. + + Returns: + Whether a process is running on the given port. + """ + return get_process_on_port(port) is not None + + def kill_process_on_port(port): """Kill the process on the given port. Args: port: The port. """ - for proc in psutil.process_iter(["pid", "name", "cmdline"]): - try: - for conns in proc.connections(kind="inet"): - if conns.laddr.port == port: - proc.kill() - return - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass + if get_process_on_port(port) is not None: + get_process_on_port(port).kill() # type: ignore + + +def terminate_port(port, _type): + """Terminate the port. + + Args: + port: The port. + _type: The type of the port. + """ + console.print( + f"Something is already running on port [bold underline]{port}[/bold underline]. This is the port the {_type} runs on." + ) + frontend_action = Prompt.ask("Kill it?", choices=["y", "n"]) + if frontend_action == "y": + kill_process_on_port(port) + else: + console.print("Exiting...") + sys.exit() def run_backend(app_name: str, loglevel: constants.LogLevel = constants.LogLevel.ERROR):