Start ComfyUI via Popen (cross-platform, no bash dependency).
()
| 80 | |
| 81 | |
| 82 | def _start_comfyui() -> int: |
| 83 | """Start ComfyUI via Popen (cross-platform, no bash dependency).""" |
| 84 | global _comfyui_proc # noqa: PLW0603 |
| 85 | log_dir = os.path.join(E2E_ROOT, "logs") |
| 86 | os.makedirs(log_dir, exist_ok=True) |
| 87 | log_path = os.path.join(log_dir, "comfyui.log") |
| 88 | # Open the log file for the subprocess. We must keep this fd open while |
| 89 | # the process runs, but MUST close it on any exit path to avoid handle |
| 90 | # leaks (particularly on Windows where open handles block rmtree). |
| 91 | log_file = open(log_path, "w") # noqa: SIM115 |
| 92 | |
| 93 | def _read_tail() -> str: |
| 94 | with open(log_path) as f: |
| 95 | return f.read()[-2000:] |
| 96 | |
| 97 | env = { |
| 98 | **os.environ, |
| 99 | "COMFYUI_PATH": COMFYUI_PATH, |
| 100 | "PYTHONUNBUFFERED": "1", |
| 101 | } |
| 102 | try: |
| 103 | _comfyui_proc = subprocess.Popen( |
| 104 | [_venv_python(), "-u", os.path.join(COMFYUI_PATH, "main.py"), |
| 105 | "--listen", "127.0.0.1", "--port", str(PORT), |
| 106 | "--cpu", "--enable-manager"], |
| 107 | stdout=log_file, stderr=subprocess.STDOUT, |
| 108 | env=env, |
| 109 | ) |
| 110 | # Wait for server to be ready. |
| 111 | # Manager may restart ComfyUI after startup dependency install (exit 0 → re-launch). |
| 112 | # If the process exits with code 0, keep polling — the restarted process will bind the port. |
| 113 | deadline = time.monotonic() + 120 |
| 114 | while time.monotonic() < deadline: |
| 115 | try: |
| 116 | r = requests.get(f"{BASE_URL}/system_stats", timeout=2) |
| 117 | if r.status_code == 200: |
| 118 | return _comfyui_proc.pid |
| 119 | except requests.ConnectionError: |
| 120 | pass |
| 121 | if _comfyui_proc.poll() is not None: |
| 122 | if _comfyui_proc.returncode != 0: |
| 123 | log_file.close() |
| 124 | raise RuntimeError( |
| 125 | f"ComfyUI exited with code {_comfyui_proc.returncode}:\n{_read_tail()}" |
| 126 | ) |
| 127 | # exit 0 = Manager restart. Keep polling for the restarted process. |
| 128 | time.sleep(1) |
| 129 | raise RuntimeError(f"ComfyUI did not start within 120s. Log:\n{_read_tail()}") |
| 130 | except Exception: |
| 131 | # Ensure log_file handle is released on any failure |
| 132 | try: |
| 133 | log_file.close() |
| 134 | except Exception: # noqa: BLE001 |
| 135 | pass |
| 136 | raise |
| 137 | |
| 138 | |
| 139 | def _stop_comfyui(): |
no test coverage detected