|
|
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) |
|
|
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(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() |