临时提交

This commit is contained in:
zw
2025-08-15 20:04:59 +08:00
parent 6332bda929
commit 690b17ec58
66 changed files with 5075 additions and 156 deletions

View File

@@ -1,10 +1,12 @@
import subprocess
import sys
import threading
import atexit
import json
import os
import socket
import time
from pathlib import Path
from typing import Optional, Union, Dict, List
class FlaskSubprocessManager:
@@ -20,38 +22,59 @@ class FlaskSubprocessManager:
def _init_manager(self):
self.process: Optional[subprocess.Popen] = None
self.comm_port = self._find_available_port()
self.comm_port = 34567
self._stop_event = threading.Event()
atexit.register(self.stop)
def _find_available_port(self):
"""动态获取可用端口"""
# 可以把 _find_available_port 留着备用,但 start 前先校验端口是否被占用
def _is_port_busy(self, port: int) -> bool:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('0.0.0.0', 0))
return s.getsockname()[1]
s.settimeout(0.2)
return s.connect_ex(("127.0.0.1", port)) == 0
# 启动flask
def start(self):
"""启动子进程Windows兼容方案"""
"""启动 Flask 子进程(兼容打包后的 exe 和源码运行"""
with self._lock:
if self.process is not None:
raise RuntimeError("子进程已在运行中!")
# 通过环境变量传递通信端口
env = os.environ.copy()
env['FLASK_COMM_PORT'] = str(self.comm_port)
env["FLASK_COMM_PORT"] = str(self.comm_port)
# —— 解析打包 exe 的稳健写法 ——
exe_path = Path(sys.executable).resolve()
if exe_path.name.lower() in ("python.exe", "pythonw.exe"):
# Nuitka 某些场景里 sys.executable 可能指向 dist\python.exe并不存在
exe_path = Path(sys.argv[0]).resolve()
is_frozen = exe_path.suffix.lower() == ".exe" and exe_path.exists()
if is_frozen:
# 打包后的 exe用当前 exe 自举
cmd = [str(exe_path), "--role=flask"]
cwd = str(exe_path.parent)
else:
# 源码运行:模块方式更稳
cmd = [sys.executable, "-m", "Module.Main", "--role=flask"]
cwd = str(Path(__file__).resolve().parent) # Module 目录
print(f"[DEBUG] spawn: {cmd} (cwd={cwd}) exists(exe)={os.path.exists(cmd[0])}")
self.process = subprocess.Popen(
['python', 'Flask/FlaskService.py'], # 启动一个子进程 FlaskService.py
stdin=subprocess.PIPE, # 标准输入流,用于向子进程发送数据
stdout=subprocess.PIPE, # 标准输出流,用于接收子进程的输出
stderr=subprocess.PIPE, # 标准错误流,用于接收子进程的错误信息
text=True, # 以文本模式打开流,否则以二进制模式打开
bufsize=1, # 缓冲区大小设置为 1表示行缓冲
encoding='utf-8', # 指定编码为 UTF-8确保控制台输出不会报错
env=env # 指定子进程的环境变量
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
errors="replace", # 新增:遇到非 UTF-8 字节用 <20> 代替,避免崩溃
bufsize=1,
env=env,
cwd=cwd,
)
print(f"Flask子进程启动 (PID: {self.process.pid}, 通信端口: {self.comm_port})")
print(f"Flask子进程启动 (PID: {self.process.pid}, 端口: {self.comm_port})")
# 将日志通过主进程输出
def print_output(stream, stream_name):
while True:
line = stream.readline()
@@ -59,7 +82,6 @@ class FlaskSubprocessManager:
break
print(f"{stream_name}: {line.strip()}")
# 启动两个线程分别处理 stdout 和 stderr
threading.Thread(target=print_output, args=(self.process.stdout, "STDOUT"), daemon=True).start()
threading.Thread(target=print_output, args=(self.process.stderr, "STDERR"), daemon=True).start()