临时提交
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user