From 7a809ae7d202f58f04accd4dfbc709071713e2cb Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Sat, 10 Oct 2026 15:33:49 +0300 Subject: [PATCH] fix: a requirement that cannot install no longer blocks the others; comfy-kitchen's sol_attn floor (0.2.32) is in requirements, so an older version is reported on update Co-Authored-By: Claude Haiku 5.5 --- core/tests/test_update.py | 19 ++++++++++++++- core/update.py | 51 +++++++++++++++++++++------------------ requirements.txt | 4 +++ 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/core/tests/test_update.py b/core/tests/test_update.py index 6a2d7b90..b2933df3 100644 --- a/core/tests/test_update.py +++ b/core/tests/test_update.py @@ -231,7 +231,24 @@ def test_a_missing_requirement_is_installed_by_its_whole_line_so_a_pin_or_url_su monkeypatch.setattr(update.subprocess, "run", lambda cmd, **k: ran.append(cmd) or subprocess.CompletedProcess(cmd, 0, "", "")) monkeypatch.setattr(update, "_pip_freeze", lambda: {}) assert update.install_requirements()["ok"] - assert ran[0][-2:] == ["zz_absent_pkg>=3.8,<3.9", "zz_model @ https://example.com/zz_model.whl"] + assert [c[-1] for c in ran] == ["zz_absent_pkg>=3.8,<3.9", "zz_model @ https://example.com/zz_model.whl"] + + +def test_one_package_that_cannot_install_does_not_stop_the_others(repo, monkeypatch): + (repo / "requirements.txt").write_text("zz_bad_url @ https://example.com/missing.whl\nzz_good_pkg\n") + monkeypatch.setattr(update, "_installed_version", lambda n: None) + ran = [] + + def fake_run(cmd, **k): + ran.append(cmd[-1]) + return subprocess.CompletedProcess(cmd, 1 if "zz_bad_url" in cmd[-1] else 0, "", "404 not found") + + monkeypatch.setattr(update.subprocess, "run", fake_run) + monkeypatch.setattr(update, "_pip_freeze", lambda: {}) + out = update.install_requirements() + assert ran == ["zz_bad_url @ https://example.com/missing.whl", "zz_good_pkg"], "the second still installs" + assert out["ok"] is False and "zz_bad_url" in out["detail"] and "404 not found" in out["detail"] + assert "zz_good_pkg" not in out["detail"].split("Could not install:")[1].split("\n")[0] def test_a_branch_switch_installs_what_is_missing_even_when_the_requirements_file_did_not_change(repo, monkeypatch): diff --git a/core/update.py b/core/update.py index d1b1fd0e..4de6d5e2 100644 --- a/core/update.py +++ b/core/update.py @@ -405,35 +405,40 @@ def install_requirements(timeout: int = 900) -> dict: + (f"\n\n{floor_note}" if floor_note else ""))} before = _pip_freeze() - # Only the absent ones, by name — never `-r requirements.txt`, which would upgrade - # anything below its floor. Dependencies are resolved normally: a new package with - # nothing under it is not installed, it is broken. The freeze diff below is what says - # whether anything already present moved as a result. - cmd = [sys.executable, "-m", "pip", "install", "--disable-pip-version-check", - *status["install"]] - try: - proc = subprocess.run(cmd, cwd=REPO_ROOT, capture_output=True, text=True, - timeout=timeout) - except subprocess.TimeoutExpired: - return {"ran": True, "ok": False, - "detail": f"pip did not finish within {timeout}s — run it yourself: " - f"pip install {' '.join(status['install'])}"} - except OSError as e: - return {"ran": True, "ok": False, "detail": f"could not run pip: {e}"} - if proc.returncode != 0: - tail = ((proc.stderr or proc.stdout or "").strip() or "pip failed")[-800:] - return {"ran": True, "ok": False, - "detail": f"pip install failed — run it yourself:\n" - f" {sys.executable} -m pip install " - f"{' '.join(status['install'])}\n\n{tail}"} + # One pip call per absent requirement, never `-r requirements.txt` (that would upgrade anything + # below its floor). Separate calls, because one call is all-or-nothing: a single package that + # cannot install (a model URL that is unreachable, a wheel missing for this Python) used to + # stop every other one from installing too. Dependencies are resolved normally: a new package + # with nothing under it is not installed, it is broken. The freeze diff says whether anything + # already present moved as a result. + failed, done = [], [] + for line in status["install"]: + cmd = [sys.executable, "-m", "pip", "install", "--disable-pip-version-check", line] + try: + proc = subprocess.run(cmd, cwd=REPO_ROOT, capture_output=True, text=True, timeout=timeout) + except subprocess.TimeoutExpired: + failed.append((line, f"pip did not finish within {timeout}s")) + continue + except OSError as e: + failed.append((line, f"could not run pip: {e}")) + continue + if proc.returncode != 0: + failed.append((line, ((proc.stderr or proc.stdout or "").strip() or "pip failed")[-400:])) + else: + done.append(line) changed = _pip_diff(before, _pip_freeze()) if changed: print("[FunPack update] pip changed these packages: " + ", ".join(changed)) + if failed: + return {"ran": True, "ok": False, "changed": changed, + "detail": ("Could not install: " + ", ".join(line for line, _ in failed) + ".\n" + + "\n".join(f"{line}:\n{tail}" for line, tail in failed) + + "\n\nTo do it yourself:\n" + + "\n".join(f" {sys.executable} -m pip install {line}" for line, _ in failed))} return {"ran": True, "ok": True, "changed": changed, "detail": (f"Installed missing: {', '.join(status['missing'])}\n" + (("Changed: " + ", ".join(changed) + "\n") if changed else "") - + (f"\n{floor_note}\n" if floor_note else "") - + (proc.stdout or "").strip()[-800:])} + + (f"\n{floor_note}\n" if floor_note else ""))} def _pip_freeze() -> dict: diff --git a/requirements.txt b/requirements.txt index 84cbb7a4..a75c8d51 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,10 @@ # Version floors ONLY where FunPack needs one, and never a bare upgrade of # anything the rest of ComfyUI compiles against. # +# SLA's sol-attn method needs comfy_kitchen's compiled sol_attn kernel, first in 0.2.32. A lower +# version is reported on update, never upgraded automatically: it is compiled against torch. +comfy-kitchen>=0.2.32 + # Reads .gguf containers in the FunPack loaders. Pure python + numpy; it does # not pull CUDA or any model. Without it a .gguf needs the ComfyUI-GGUF node # pack instead.