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 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-10-10 15:33:49 +03:00
co-authored by Claude Haiku 5.5
parent 7aa571000a
commit 7a809ae7d2
3 changed files with 50 additions and 24 deletions
+18 -1
View File
@@ -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):
+28 -23
View File
@@ -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:
+4
View File
@@ -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.