Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions comfy_cli/command/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ def execute(
gpu=gpu,
cuda_version=cuda_version.value,
rocm_version=rocm_version.value,
skip_torch=skip_torch_or_directml,
)
depComp.compile_deps()
depComp.install_deps()
Expand Down
8 changes: 7 additions & 1 deletion comfy_cli/uv.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ def __init__(
extraSpecs: list[str] | None = None,
cuda_version: str | None = None,
rocm_version: str | None = None,
skip_torch: bool = False,
):
"""Compiler/installer of Python dependencies based on uv

Expand All @@ -379,16 +380,21 @@ def __init__(
reqFilesCore (Optional[list[PathLike]]): list of core requirement files (requirements.txt, pyproject.toml, etc) to be included in the compilation. Any requirements determined from these files will override all other requirements
reqFilesExt (Optional[list[PathLike]]): list of requirement files (requirements.txt, pyproject.toml, etc) to be included in the compilation
extraSpecs (Optional[list[str]]): list of extra Python requirement specifiers to be included in the compilation
skip_torch (bool): if True, skip torch/torchvision/torchaudio installation and GPU index URLs
"""
self.cwd = Path(cwd).expanduser().resolve()
self.outDir: Path = Path(outDir).expanduser().resolve()
# use .absolute since .resolve breaks the softlink-is-interpreter assumption of venvs
self.executable = Path(executable).expanduser().absolute()
self.gpu = DependencyCompiler.Resolve_Gpu(gpu)
self.skip_torch = skip_torch
self.reqFiles = [Path(reqFile) for reqFile in reqFilesExt] if reqFilesExt is not None else None
self.extraSpecs = [] if extraSpecs is None else extraSpecs

if self.gpu == GPU_OPTION.NVIDIA:
if self.skip_torch:
self.gpuUrl = None
self.torchBackend = None
elif self.gpu == GPU_OPTION.NVIDIA:
tag = f"cu{cuda_version.replace('.', '')}" if cuda_version else DependencyCompiler.nvidiaTorchBackend
self.gpuUrl = f"https://download.pytorch.org/whl/{tag}"
self.torchBackend = tag
Expand Down
30 changes: 30 additions & 0 deletions tests/comfy_cli/test_install_python_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,36 @@ def test_fast_deps_passes_python_to_dependency_compiler(self, tmp_path):
MockCompiler.Install_Build_Deps.assert_called_once_with(executable="/resolved/python")
MockCompiler.assert_called_once()
assert MockCompiler.call_args[1]["executable"] == "/resolved/python"
assert MockCompiler.call_args[1].get("skip_torch") in (None, False)

def test_fast_deps_forwards_skip_torch(self, tmp_path):
repo_dir = str(tmp_path)

with (
patch("comfy_cli.command.install.ensure_workspace_python", return_value="/resolved/python"),
patch("comfy_cli.command.install.clone_comfyui"),
patch("comfy_cli.command.install.check_comfy_repo", return_value=(True, None)),
patch("comfy_cli.command.install.DependencyCompiler") as MockCompiler,
patch("comfy_cli.command.install.WorkspaceManager"),
patch.object(install.workspace_manager, "skip_prompting", True),
patch.object(install.workspace_manager, "setup_workspace_manager"),
):
MockCompiler.Install_Build_Deps = MagicMock()
mock_instance = MagicMock()
MockCompiler.return_value = mock_instance

install.execute(
url="https://github.com/test/test.git",
manager_url="https://github.com/test/manager.git",
comfy_path=repo_dir,
restore=False,
skip_manager=True,
version="nightly",
fast_deps=True,
skip_torch_or_directml=True,
)

assert MockCompiler.call_args[1]["skip_torch"] is True


def _get_torch_install_cmd(calls):
Expand Down
19 changes: 12 additions & 7 deletions tests/e2e/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,25 @@ def test_node(comfy_cli, workspace):
break
assert proc.returncode == 0, f"node install failed after 3 attempts:\n{proc.stderr}"

proc = exec(
f"""
{comfy_cli} node reinstall {node}
"""
)
assert proc.returncode == 0
for attempt in range(3):
proc = exec(
f"""
{comfy_cli} node reinstall {node}
"""
)
if proc.returncode == 0:
break
assert proc.returncode == 0, f"node reinstall failed after 3 attempts:\n{proc.stderr}"

proc = exec(
f"""
{comfy_cli} node show all
"""
)
assert proc.returncode == 0
assert node in proc.stdout
# cm-cli may display the repo name (ComfyUI-AnimateDiff-Evolved) rather
# than the registry id (comfyui-animatediff-evolved), so compare lowercase.
assert node.lower() in proc.stdout.lower()

proc = exec(
f"""
Expand Down
32 changes: 32 additions & 0 deletions tests/uv/test_uv.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,35 @@ def test_amd_custom_rocm_version():
)
assert depComp.torchBackend == "rocm7.1"
assert depComp.gpuUrl == "https://download.pytorch.org/whl/rocm7.1"


@pytest.mark.parametrize("gpu", [GPU_OPTION.NVIDIA, GPU_OPTION.AMD, GPU_OPTION.CPU])
def test_skip_torch_disables_gpu_url_and_backend(gpu):
depComp = DependencyCompiler(cwd=temp, gpu=gpu, outDir=temp, reqFilesCore=[], reqFilesExt=[], skip_torch=True)
assert depComp.torchBackend is None
assert depComp.gpuUrl is None


def test_skip_torch_override_has_no_torch():
depComp = DependencyCompiler(
cwd=temp,
gpu=GPU_OPTION.NVIDIA,
outDir=temp,
reqFilesCore=[mockReqsDir / "core_reqs.txt"],
reqFilesExt=[],
skip_torch=True,
)
depComp.make_override()
content = depComp.override.read_text()
assert "torch" not in content


def test_skip_torch_install_deps_no_extra_index_url():
depComp = DependencyCompiler(
cwd=temp, gpu=GPU_OPTION.NVIDIA, outDir=temp, reqFilesCore=[], reqFilesExt=[], skip_torch=True
)
depComp.out.write_text("requests==2.31.0\n")
with patch("comfy_cli.uv._check_call") as mock_check_call:
depComp.install_deps()
cmd = mock_check_call.call_args[0][0]
assert "--extra-index-url" not in cmd
Loading