""" PyServe Daemon Process Runs pyserve services in background mode. """ import argparse import asyncio import logging import os import signal import sys from pathlib import Path from types import FrameType from typing import Optional def main() -> None: parser = argparse.ArgumentParser(description="PyServe Daemon") parser.add_argument("--config", required=True, help="Configuration file path") parser.add_argument("--state-dir", required=True, help="State directory path") parser.add_argument("--services", default=None, help="Comma-separated list of services") parser.add_argument("--scale", action="append", default=[], help="Scale overrides (name=workers)") parser.add_argument("--force-recreate", action="store_true", help="Force recreate services") args = parser.parse_args() config_path = Path(args.config) state_dir = Path(args.state_dir) services = args.services.split(",") if args.services else None scale_map = {} for scale in args.scale: name, workers = scale.split("=") scale_map[name] = int(workers) from ..config import Config config = Config.from_yaml(str(config_path)) from .state import StateManager state_manager = StateManager(state_dir) log_file = state_dir / "logs" / "daemon.log" log_file.parent.mkdir(parents=True, exist_ok=True) logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[ logging.FileHandler(log_file), ], ) logger = logging.getLogger("pyserve.daemon") pid_file = state_dir / "pyserve.pid" pid_file.write_text(str(os.getpid())) logger.info(f"Starting daemon with PID {os.getpid()}") from ._runner import ServiceRunner runner = ServiceRunner(config, state_manager) def signal_handler(signum: int, frame: Optional[FrameType]) -> None: logger.info(f"Received signal {signum}, shutting down...") runner.stop() signal.signal(signal.SIGTERM, signal_handler) signal.signal(signal.SIGINT, signal_handler) try: asyncio.run( runner.start( services=services, scale_map=scale_map, force_recreate=args.force_recreate, ) ) except Exception as e: logger.error(f"Daemon error: {e}") sys.exit(1) finally: if pid_file.exists(): pid_file.unlink() logger.info("Daemon stopped") if __name__ == "__main__": main()