import signal from typing import Literal import subprocess import time import threading import os import signal from environment import * class AppRunner: def __init__(self, run_type: RunType): self.run_type = run_type self.sub_proc = None def start(self): print(f"start app by type: {self.run_type}") if self.run_type==RunType.electron: cmd_args = [APP_PATH, f"--remote-debugging-port={DEBUG_PORT}"] cwd = None log_file = APP_LOG elif self.run_type == RunType.code: cmd_args = ["python", str(CODE_PATH)] cwd = CODE_DIR log_file = CODE_LOG elif self.run_type == RunType.dev: cmd_args = ["python", str(DEV_PATH)] cwd = DEV_DIR log_file = DEV_LOG else: raise TypeError(f"invalid run_type: {self.run_type}") self.clear_log(log_file) self.sub_proc = subprocess.Popen( cmd_args, cwd=cwd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, preexec_fn= os.setsid ) print(f"translator started, PID: {self.sub_proc.pid}") time.sleep(10) # 等待app 启动 self.wait_for_app_start(log_file) print(f"translator is ready.") return self.sub_proc def clear_log(self, log_path: Path): if log_path.exists(): log_path.unlink(missing_ok=True) def wait_for_app_start(self, log_file, timeout = 60, interval=3): for i in range(int(timeout/interval)): ret = subprocess.run(f"tail -n 10 {log_file}", check=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) if "Pipeline is ready" in ret.stdout: return True # print(ret.stdout) print(f"app is not started yet, retry {i+1}...") time.sleep(interval) print(ret.stdout) self.stop() raise RuntimeError(f"app not started in {timeout}s") def stop(self): pgid = os.getpgid(self.sub_proc.pid) os.killpg(pgid, signal.SIGTERM) try: time.sleep(3) return_code = self.sub_proc.wait(timeout=10) print(f"进程组已终止,进程退出码: {return_code}") except subprocess.TimeoutExpired: print("超时,进程组可能未完全终止,尝试 SIGKILL...") os.killpg(pgid, signal.SIGKILL) self.sub_proc.wait(timeout=10) print("进程组已被强制杀死。") except Exception as e: print("停止进程失败:", e) if __name__ == '__main__': app = AppRunner(RunType.code) app.start() print(app.wait_for_app_start()) app.stop()