Files
order_system/tools/packaged_server.py
reddaniel110 768b4705dc 新增订单收票付款交货跟进并完善使用升级手册
增加部分付款和部分交货状态、工作台筛选、权限校验、跟进记录及 v4 数据迁移;补充 Windows EXE 构建与验收脚本。附 16 页 PDF 使用升级手册及企业微信详细配置说明、生成脚本和测试截图。验证:76 项回归测试、EXE 浏览器验收与 PDF 逐页校验通过。
2026-09-16 13:40:43 +08:00

66 lines
2.8 KiB
Python

"""Run an isolated copy of the EXE, with no Python installation on its PATH."""
import os
import shutil
import socket
import subprocess
import time
import urllib.error
import urllib.request
from pathlib import Path
class PackagedServer:
def __init__(self, executable, directory):
source = Path(executable).resolve(strict=True)
self.directory = Path(directory).resolve()
self.executable = self.directory / source.name
shutil.copy2(source, self.executable)
self.cwd = self.directory / '不同启动目录'
self.cwd.mkdir()
with socket.socket() as sock:
sock.bind(('127.0.0.1', 0))
self.port = sock.getsockname()[1]
self.base = 'http://127.0.0.1:%s' % self.port
self.process = None
self.log = None
self.http = urllib.request.build_opener(urllib.request.ProxyHandler({}))
def start(self):
env = {key: value for key, value in os.environ.items()
if not key.upper().startswith(('PO_', 'PYTHON', 'VIRTUAL_ENV'))}
windows = Path(env.get('SystemRoot', r'C:\Windows'))
env.update(PO_HOST='127.0.0.1', PO_OPEN_BROWSER='0',
PATH=os.pathsep.join((str(windows / 'System32'), str(windows))))
self.log = (self.directory / 'exe.log').open('ab')
self.process = subprocess.Popen([str(self.executable), str(self.port)],
cwd=self.cwd, env=env, stdin=subprocess.DEVNULL,
stdout=self.log, stderr=subprocess.STDOUT,
creationflags=subprocess.CREATE_NO_WINDOW)
deadline = time.monotonic() + 90
while time.monotonic() < deadline:
if self.process.poll() is not None:
raise RuntimeError('EXE exited during startup; see %s' % self.log.name)
try:
with self.http.open(self.base + '/login', timeout=1) as response:
if response.status == 200:
return
except (urllib.error.URLError, TimeoutError, ConnectionError):
pass
time.sleep(0.2)
raise TimeoutError('EXE startup timed out; see %s' % self.log.name)
def close(self):
if self.process is not None and self.process.poll() is None:
# A one-file PyInstaller app has a launcher and a server child.
# Terminate only the process tree created by this test.
subprocess.run(['taskkill', '/PID', str(self.process.pid), '/T', '/F'],
check=True, capture_output=True,
creationflags=subprocess.CREATE_NO_WINDOW)
self.process.wait(timeout=15)
if self.log:
self.log.close()
def restart(self):
self.close()
self.start()