From 8e00caff4c21ae4c458359fc388bdf19e2467352 Mon Sep 17 00:00:00 2001 From: Izalia Mae Date: Wed, 20 Mar 2024 10:59:50 -0400 Subject: [PATCH] add dev run command --- dev.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/dev.py b/dev.py index 604032d..83fa707 100755 --- a/dev.py +++ b/dev.py @@ -4,7 +4,9 @@ import shlex import shutil import subprocess import sys +import time +from datetime import datetime from gemi import __version__ from pathlib import Path from tempfile import TemporaryDirectory @@ -12,15 +14,23 @@ from tempfile import TemporaryDirectory try: import click -except ImportError: - proc = subprocess.run([sys.executable, "-m", "pip", "install", "click"], check = False) + from watchdog.observers import Observer + from watchdog.events import PatternMatchingEventHandler - if proc.returncode != 0: +except ImportError: + CMD = f"{sys.executable} -m pip install watchdog click" + PROC = subprocess.run(shlex.split(CMD), check = False) + + if PROC.returncode != 0: sys.exit() - print("Successfully installed click") + print("Successfully installed click and watchdog") + import click + from watchdog.observers import Observer + from watchdog.events import PatternMatchingEventHandler + REPO = Path(__file__).resolve().parent @@ -93,5 +103,85 @@ def cli_install(): subprocess.run(shlex.split(f"{sys.executable} -m pip install -e .[dev,doc]"), check = False) +@cli.command("run") +def cli_run(): + print("Starting process watcher") + + handler = WatchHandler() + handler.run_proc() + + watcher = Observer() + watcher.schedule(handler, str(REPO.joinpath("gemi")), recursive=True) + watcher.start() + + try: + while True: + handler.proc.stdin.write(sys.stdin.read().encode("UTF-8")) # type: ignore + handler.proc.stdin.flush() # type: ignore + + except KeyboardInterrupt: + pass + + handler.kill_proc() + watcher.stop() + watcher.join() + + +class WatchHandler(PatternMatchingEventHandler): + patterns = ["*.py"] + cmd = [sys.executable, "-m", "gemi.server"] + + + def __init__(self): + PatternMatchingEventHandler.__init__(self) + + self.proc: subprocess.Popen | None = None + self.last_restart: datetime | None = None + + + def kill_proc(self): + if not self.proc or self.proc.poll() is not None: + return + + print(f"Terminating process {self.proc.pid}") + self.proc.terminate() + sec = 0.0 + + while self.proc.poll() is None: + time.sleep(0.1) + sec += 0.1 + + if sec >= 5: + print("Failed to terminate. Killing process...") + self.proc.kill() + break + + print("Process terminated") + + + def run_proc(self, restart=False): + timestamp = datetime.timestamp(datetime.now()) + self.last_restart = timestamp if not self.last_restart else 0 + + if restart: + if timestamp - 3 < self.last_restart: + return + + self.kill_proc() + + self.proc = subprocess.Popen(self.cmd, stdin = subprocess.PIPE) + self.last_restart = timestamp + + print(f"Started process with PID {self.proc.pid}", self.proc.pid) + print("Command:", " ".join(self.cmd)) + + + def on_any_event(self, event): + if event.event_type not in ["modified", "created", "deleted"]: + return + + self.run_proc(restart = True) + + if __name__ == "__main__": cli()