""" pyserve down - Stop all services """ import signal import time from pathlib import Path from typing import Any, cast import click @click.command("down") @click.option( "--timeout", "timeout", default=30, type=int, help="Timeout in seconds for graceful shutdown", ) @click.option( "-v", "--volumes", is_flag=True, help="Remove volumes/data", ) @click.option( "--remove-orphans", is_flag=True, help="Remove orphaned services", ) @click.pass_obj def down_cmd( ctx: Any, timeout: int, volumes: bool, remove_orphans: bool, ) -> None: """ Stop and remove all services. \b Examples: pyserve down # Stop all services pyserve down --timeout 60 # Extended shutdown timeout pyserve down -v # Remove volumes too """ from ..output import console, print_error, print_info, print_success, print_warning from ..state import StateManager state_manager = StateManager(Path(".pyserve"), ctx.project) if state_manager.is_daemon_running(): daemon_pid = state_manager.get_daemon_pid() console.print(f"[bold]Stopping PyServe daemon (PID: {daemon_pid})...[/bold]") try: import os # FIXME: Please fix the cast usage here os.kill(cast(int, daemon_pid), signal.SIGTERM) start_time = time.time() while time.time() - start_time < timeout: try: # FIXME: Please fix the cast usage here os.kill(cast(int, daemon_pid), 0) time.sleep(0.5) except ProcessLookupError: break else: print_warning("Graceful shutdown timed out, forcing...") try: # FIXME: Please fix the cast usage here os.kill(cast(int, daemon_pid), signal.SIGKILL) except ProcessLookupError: pass state_manager.clear_daemon_pid() print_success("PyServe daemon stopped") except ProcessLookupError: print_info("Daemon was not running") state_manager.clear_daemon_pid() except PermissionError: print_error("Permission denied to stop daemon") raise click.Abort() else: services = state_manager.get_all_services() if not services: print_info("No services are running") return console.print("[bold]Stopping services...[/bold]") from ...config import Config from .._runner import ServiceRunner config_path = Path(ctx.config_file) if config_path.exists(): config = Config.from_yaml(str(config_path)) else: config = Config() runner = ServiceRunner(config, state_manager) import asyncio try: asyncio.run(runner.stop_all(timeout=timeout)) print_success("All services stopped") except Exception as e: print_error(f"Error stopping services: {e}") if volumes: console.print("Cleaning up state...") state_manager.clear() print_info("State cleared") if remove_orphans: # This would remove services that are in state but not in config pass