Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a --fast-deps option to comfy install/reinstall that enables new faster/unified/uv-based dependency install #141

Merged
merged 30 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7e61541
added uv script
telamonian Jul 12, 2024
3f0b106
--fast-deps option now works with installing comfyui core and comfyui…
telamonian Jul 12, 2024
4f08f72
`comfy node install --fast-deps` is now supported
telamonian Jul 19, 2024
3449db5
`--fast-deps` installs of core/nodes now auto-detect gpu if `torch` i…
telamonian Jul 19, 2024
66bcfdb
added section on developing comfy-cli and ComfyUI-Manager together
telamonian Jul 19, 2024
2a57a70
clean up specification of --fast-deps option
telamonian Jul 26, 2024
4cc4241
fix python 3.9 incompatible type hint syntax
telamonian Jul 26, 2024
d0c271d
picked lint
telamonian Jul 26, 2024
09f1883
post rebase cleanup
telamonian Aug 2, 2024
54f3d40
make `DependencyCompiler` more flexible/testable
telamonian Aug 2, 2024
de64035
fix routine for finding custom node dirs
telamonian Aug 2, 2024
00e447e
remove awkward `fastInstallComfyDeps` shim function
telamonian Aug 2, 2024
0773ed0
mark `staticmethod`s with UpperCamelCase names
telamonian Aug 2, 2024
5baac22
cleanup `DependencyCompiler.InstallBuildDeps`
telamonian Aug 2, 2024
a7866ad
started adding tests for code in `uv.py` module
telamonian Aug 2, 2024
643f4ae
test_uv now runs, dies on mock extension vs extension dependency conf…
telamonian Aug 2, 2024
73396ce
`uv` subprocess calls now print cmd/output on error
telamonian Aug 2, 2024
17e2e47
add parsing of `uv compile` conflict error
telamonian Aug 9, 2024
c5d696b
add `resolve_strategy="ask"` option to `DependencyCompiler.Compile`
telamonian Aug 9, 2024
2efb3a7
basic implementation of ext-vs-ext conflict resolution via manual use…
telamonian Aug 9, 2024
c92fc85
improved feedback/UX of manual dependency conflict resolution
telamonian Aug 9, 2024
699505e
fixup user input for conflict resolution; fixup test-uv paths
telamonian Aug 9, 2024
2825212
`test_compile` in `test_uv` now works/runs correctly via pytest
telamonian Aug 9, 2024
4295769
added `assert` to `test_compile`. Might still be a bit fragile
telamonian Aug 13, 2024
98b6238
made `test_compile` more robust
telamonian Aug 15, 2024
5c9b379
small type hint fix
telamonian Aug 15, 2024
5a81f65
mark old py dep install funcs with `pip_` prefix
telamonian Aug 15, 2024
41de4c5
in `uv.py`, make func/methods `snake_case`, static methods `Snake_Case`
telamonian Aug 16, 2024
c87cb3d
ci test fix
telamonian Aug 16, 2024
ce67400
disable nuisance lint rule (W0707)
telamonian Aug 16, 2024
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ __pycache__/
.vscode/settings.json
.idea/
.vscode/
*.code-workspace
.history

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down Expand Up @@ -45,6 +47,9 @@ share/python-wheels/
*.manifest
*.spec

# temporary files created by tests
tests/temp/

venv/

bisect_state.json
2 changes: 2 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ disable=
W0212, # protected-access
C0301, # line-too-long
C0103, # invalid-name
W1510, # subprocess-run-check
W0707, # raise-missing-from

# TODO
W3101, # missing timeout on request
Expand Down
20 changes: 20 additions & 0 deletions DEV_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ def remove(name: str):
- Use `rich` for all console output
- For progress reporting, use either [`rich.progress`](https://rich.readthedocs.io/en/stable/progress.html)

## Develop comfy-cli and ComfyUI-Manager (cm-cli) together
### Make changes to both
1. Fork your own branches of `comfy-cli` and `ComfyUI-Manager`, make changes
2. Be sure to commit any changes to `ComfyUI-Manager` to a new branch, and push to remote
telamonian marked this conversation as resolved.
Show resolved Hide resolved

### Try out changes to both
1. clone the changed branch of `comfy-cli`, then live install `comfy-cli`:
- `pip install -e comfy-cli`
2. Go to a test dir and run:
- `comfy --here install --manager-url=<path-or-url-to-fork-of-ComfyUI-Manager>`
3. Run:
- `cd ComfyUI/custom_nodes/ComfyUI-Manager/ && git checkout <changed-branch> && cd -`
4. Further changes can be pulled into these copies of the `comfy-cli` and `ComfyUI-Manager` repos

### Debug both simultaneously
1. Follow instructions above to get working install with changes
2. Add breakpoints directly to code: `import ipdb; ipdb.set_trace()`
3. Execute relevant `comfy-cli` command


## Contact

If you have any questions or need further assistance, please contact the project maintainer at [???](mailto:???@drip.art).
Expand Down
10 changes: 10 additions & 0 deletions comfy_cli/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,14 @@ def install(
commit: Annotated[
Optional[str], typer.Option(help="Specify commit hash for ComfyUI")
] = None,
fast_deps: Annotated[
Optional[bool],
typer.Option(
"--fast-deps",
show_default=False,
help="Use new fast dependency installer",
),
] = False,
):
check_for_updates()
checker = EnvChecker()
Expand Down Expand Up @@ -260,6 +268,7 @@ def install(
plat=platform,
skip_torch_or_directml=skip_torch_or_directml,
skip_requirement=skip_requirement,
fast_deps=fast_deps,
)
print(f"ComfyUI is installed at: {comfy_path}")
return None
Expand Down Expand Up @@ -331,6 +340,7 @@ def install(
plat=platform,
skip_torch_or_directml=skip_torch_or_directml,
skip_requirement=skip_requirement,
fast_deps=fast_deps,
)

print(f"ComfyUI is installed at: {comfy_path}")
Expand Down
18 changes: 17 additions & 1 deletion comfy_cli/command/custom_nodes/cm_cli_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
from rich import print

from comfy_cli.config_manager import ConfigManager
from comfy_cli.uv import DependencyCompiler
from comfy_cli.workspace_manager import WorkspaceManager

workspace_manager = WorkspaceManager()

# set of commands that invalidate (ie require an update of) dependencies after they are run
_dependency_cmds = {
'install',
'reinstall',
}

def execute_cm_cli(args, channel=None, mode=None) -> str | None:
def execute_cm_cli(args, channel=None, fast_deps=False, mode=None) -> str | None:
_config_manager = ConfigManager()

workspace_path = workspace_manager.workspace_path
Expand All @@ -34,9 +40,13 @@ def execute_cm_cli(args, channel=None, mode=None) -> str | None:
raise typer.Exit(code=1)

cmd = [sys.executable, cm_cli_path] + args

if channel is not None:
cmd += ["--channel", channel]

if fast_deps:
cmd += ["--no-deps"]

if mode is not None:
cmd += ["--mode", mode]

Expand All @@ -54,6 +64,12 @@ def execute_cm_cli(args, channel=None, mode=None) -> str | None:
cmd, env=new_env, check=True, capture_output=True, text=True
)
print(result.stdout)

if fast_deps and args[0] in _dependency_cmds:
# we're using the fast_deps behavior and just ran a command that invalidated the dependencies
depComp = DependencyCompiler(cwd=workspace_path)
depComp.install_comfy_deps()

return result.stdout
except subprocess.CalledProcessError as e:
if e.returncode == 1:
Expand Down
36 changes: 26 additions & 10 deletions comfy_cli/command/custom_nodes/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ def show(

validate_mode(mode)

execute_cm_cli(["show", arg], channel, mode)
execute_cm_cli(["show", arg], channel=channel, mode=mode)


@app.command("simple-show", help="Show node list (simple mode)")
Expand Down Expand Up @@ -402,7 +402,7 @@ def simple_show(

validate_mode(mode)

execute_cm_cli(["simple-show", arg], channel, mode)
execute_cm_cli(["simple-show", arg], channel=channel, mode=mode)


# install, reinstall, uninstall
Expand All @@ -420,6 +420,14 @@ def install(
autocompletion=channel_completer,
),
] = None,
fast_deps: Annotated[
Optional[bool],
typer.Option(
"--fast-deps",
show_default=False,
help="Use new fast dependency installer",
),
] = False,
mode: str = typer.Option(
None,
help="[remote|local|cache]",
Expand All @@ -432,7 +440,7 @@ def install(

validate_mode(mode)

execute_cm_cli(["install"] + nodes, channel, mode)
execute_cm_cli(["install"] + nodes, channel=channel, fast_deps=fast_deps, mode=mode)


@app.command(help="Reinstall custom nodes")
Expand All @@ -449,6 +457,14 @@ def reinstall(
autocompletion=channel_completer,
),
] = None,
fast_deps: Annotated[
Optional[bool],
typer.Option(
"--fast-deps",
show_default=False,
help="Use new fast dependency installer",
),
] = False,
mode: str = typer.Option(
None,
help="[remote|local|cache]",
Expand All @@ -461,7 +477,7 @@ def reinstall(

validate_mode(mode)

execute_cm_cli(["reinstall"] + nodes, channel, mode)
execute_cm_cli(["reinstall"] + nodes, channel=channel, fast_deps=fast_deps, mode=mode)


@app.command(help="Uninstall custom nodes")
Expand Down Expand Up @@ -490,7 +506,7 @@ def uninstall(

validate_mode(mode)

execute_cm_cli(["uninstall"] + nodes, channel, mode)
execute_cm_cli(["uninstall"] + nodes, channel=channel, mode=mode)


def update_node_id_cache():
Expand Down Expand Up @@ -544,7 +560,7 @@ def update(
):
validate_mode(mode)

execute_cm_cli(["update"] + nodes, channel, mode)
execute_cm_cli(["update"] + nodes, channel=channel, mode=mode)

update_node_id_cache()

Expand Down Expand Up @@ -573,7 +589,7 @@ def disable(
):
validate_mode(mode)

execute_cm_cli(["disable"] + nodes, channel, mode)
execute_cm_cli(["disable"] + nodes, channel=channel, mode=mode)


@app.command(help="Enable custom nodes")
Expand All @@ -600,7 +616,7 @@ def enable(
):
validate_mode(mode)

execute_cm_cli(["enable"] + nodes, channel, mode)
execute_cm_cli(["enable"] + nodes, channel=channel, mode=mode)


@app.command(help="Fix dependencies of custom nodes")
Expand All @@ -627,7 +643,7 @@ def fix(
):
validate_mode(mode)

execute_cm_cli(["fix"] + nodes, channel, mode)
execute_cm_cli(["fix"] + nodes, channel=channel, mode=mode)


@app.command(
Expand Down Expand Up @@ -685,7 +701,7 @@ def install_deps(
else:
deps_file = os.path.abspath(os.path.expanduser(deps))

execute_cm_cli(["install-deps", deps_file], channel, mode)
execute_cm_cli(["install-deps", deps_file], channel=channel, mode=mode)

if tmp_path is not None and os.path.exists(tmp_path):
os.remove(tmp_path)
Expand Down
27 changes: 17 additions & 10 deletions comfy_cli/command/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from comfy_cli import constants, ui, utils
from comfy_cli.command.custom_nodes.command import update_node_id_cache
from comfy_cli.constants import GPU_OPTION
from comfy_cli.uv import DependencyCompiler
from comfy_cli.workspace_manager import WorkspaceManager, check_comfy_repo

workspace_manager = WorkspaceManager()
Expand All @@ -20,7 +21,7 @@ def get_os_details():
return os_name, os_version


def install_comfyui_dependencies(
def pip_install_comfyui_dependencies(
repo_dir,
gpu: GPU_OPTION,
plat: constants.OS,
Expand Down Expand Up @@ -150,7 +151,7 @@ def install_comfyui_dependencies(


# install requirements for manager
def install_manager_dependencies(repo_dir):
def pip_install_manager_dependencies(repo_dir):
os.chdir(os.path.join(repo_dir, "custom_nodes", "ComfyUI-Manager"))
subprocess.run(
[sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], check=True
Expand All @@ -169,6 +170,7 @@ def execute(
plat: constants.OS = None,
skip_torch_or_directml: bool = False,
skip_requirement: bool = False,
fast_deps: bool = False,
*args,
**kwargs,
):
Expand All @@ -192,7 +194,6 @@ def execute(
if "@" in url:
# clone specific branch
url, branch = url.rsplit("@", 1)

subprocess.run(["git", "clone", "-b", branch, url, repo_dir], check=True)
else:
subprocess.run(["git", "clone", url, repo_dir], check=True)
Expand All @@ -208,9 +209,10 @@ def execute(
os.chdir(repo_dir)
subprocess.run(["git", "checkout", commit], check=True)

install_comfyui_dependencies(
repo_dir, gpu, plat, cuda_version, skip_torch_or_directml, skip_requirement
)
if not fast_deps:
pip_install_comfyui_dependencies(
repo_dir, gpu, plat, cuda_version, skip_torch_or_directml, skip_requirement
)

WorkspaceManager().set_recent_workspace(repo_dir)
workspace_manager.setup_workspace_manager(specified_workspace=repo_dir)
Expand All @@ -224,8 +226,8 @@ def execute(
manager_repo_dir = os.path.join(repo_dir, "custom_nodes", "ComfyUI-Manager")

if os.path.exists(manager_repo_dir):
if restore:
install_manager_dependencies(repo_dir)
if restore and not fast_deps:
pip_install_manager_dependencies(repo_dir)
else:
print(
f"Directory {manager_repo_dir} already exists. Skipping installation of ComfyUI-Manager.\nIf you want to restore dependencies, add the '--restore' option."
Expand All @@ -236,13 +238,18 @@ def execute(
if "@" in manager_url:
# clone specific branch
manager_url, manager_branch = manager_url.rsplit("@", 1)

subprocess.run(["git", "clone", "-b", manager_branch, manager_url, manager_repo_dir], check=True)
else:
subprocess.run(["git", "clone", manager_url, manager_repo_dir], check=True)

install_manager_dependencies(repo_dir)
if not fast_deps:
pip_install_manager_dependencies(repo_dir)

if fast_deps:
depComp = DependencyCompiler(cwd=repo_dir, gpu=gpu)
depComp.install_comfy_deps()

if not skip_manager:
update_node_id_cache()

os.chdir(repo_dir)
Expand Down
27 changes: 26 additions & 1 deletion comfy_cli/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,33 @@ def show_progress(iterable, total, description="Downloading..."):

ChoiceType = Union[str, Choice, Dict[str, Any]]

def prompt_autocomplete(
question: str,
choices: List[ChoiceType],
default: ChoiceType = "",
force_prompting: bool = False
) -> Optional[ChoiceType]:
"""
Asks a single select question using questionary and returns the selected response.

Args:
question (str): The question to display to the user.
choices (List[ChoiceType]): A list of choices the user can autocomplete from.
default (ChoiceType): Default choice.
force_prompting (bool): Whether to force prompting even if skip_prompting is set.

Returns:
Optional[ChoiceType]: The selected choice from the user, or None if skipping prompts.
"""
if workspace_manager.skip_prompting and not force_prompting:
return None
return questionary.autocomplete(question, choices=choices, default=default).ask()


def prompt_select(
question: str,
choices: List[ChoiceType],
default: ChoiceType = "",
force_prompting: bool = False
) -> Optional[ChoiceType]:
"""
Expand All @@ -47,14 +71,15 @@ def prompt_select(
Args:
question (str): The question to display to the user.
choices (List[ChoiceType]): A list of choices for the user to select from.
default (ChoiceType): Default choice.
force_prompting (bool): Whether to force prompting even if skip_prompting is set.

Returns:
Optional[ChoiceType]: The selected choice from the user, or None if skipping prompts.
"""
if workspace_manager.skip_prompting and not force_prompting:
return None
return questionary.select(question, choices=choices).ask()
return questionary.select(question, choices=choices, default=default).ask()


E = TypeVar('E', bound=Enum)
Expand Down
Loading
Loading