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