From f423b169c3b7fe3000be3d6f150f43a80b37bf52 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 2 Aug 2024 13:14:30 -0400 Subject: [PATCH 01/10] added ruff and cleaned up `pyproject.toml` --- pyproject.toml | 59 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 75e6de7..b1403fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,31 +15,36 @@ maintainers = [ {name = "Yoland Yan", email = "yoland@drip.art"}, {name = "James Kwon", email = "hongilkwon316@gmail.com"}, {name = "Robin Huang", email = "robin@drip.art"}, - {name = "Dr.Lt.Data", email = "dr.lt.data@gmail.com"} -] - -dependencies = [ - "typer>=0.9.0", - "GitPython", - "rich", - "requests", - "charset-normalizer>=3.0.0", - "pyyaml", - "typing-extensions>=4.7.0", - "mixpanel", - "questionary", - "psutil", - "tomlkit", - "pathspec", - "httpx", - "packaging", - "websocket-client" + {name = "Dr.Lt.Data", email = "dr.lt.data@gmail.com"}, ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)" + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", +] + +dependencies = [ + "charset-normalizer>=3.0.0", + "GitPython", + "httpx", + "mixpanel", + "packaging", + "pathspec", + "psutil", + "pyyaml", + "questionary", + "requests", + "rich", + "tomlkit", + "typer>=0.9.0", + "typing-extensions>=4.7.0", + "websocket-client", +] + +[project.optional-dependencies] +dev = [ + "ruff", ] [project.scripts] @@ -54,6 +59,14 @@ Repository = "https://github.com/drip-art/comfy-cli.git" where = ["."] include = ["comfy_cli*"] -[tool.isort] -profile = "black" -line_length = 88 +[tool.ruff] +line-length = 150 +target-version = "py39" + +select = [ + "E4", # default + "E7", # default + "E9", # default + "F", # default + "I", # isort-like behavior (import statement sorting) +] From 9e80432227e6e6e319ddc81e337dda08c398075f Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 2 Aug 2024 13:58:10 -0400 Subject: [PATCH 02/10] remove unneeded project config files --- .pylintrc | 52 ++++++++++++++++++++---------------------- pylint_formatter.sh | 12 ---------- pyproject.toml | 1 + requirements.txt | 15 ------------ tests/requirements.txt | 1 - 5 files changed, 26 insertions(+), 55 deletions(-) delete mode 100755 pylint_formatter.sh delete mode 100644 requirements.txt delete mode 100644 tests/requirements.txt diff --git a/.pylintrc b/.pylintrc index a31e900..873ec59 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,30 +1,28 @@ -[MASTER] -disable= - C0114, # missing-module-docstring - C0115, # missing-class-docstring - C0116, # missing-function-docstring - C0209, # consider-using-f-string - W0622, # redefined-builtin - W1113, # too-many-arguments - W0613, # unused-argument - W0718, # broad-exception-caught - W0511, # fixme - W0621, # redefined-outer-name - W1514, # unspecified-encoding - W0603, # global-statement - W1203, # logging-fstring-interpolation - W0212, # protected-access - C0301, # line-too-long - C0103, # invalid-name - W1510, # subprocess-run-check - W0707, # raise-missing-from +; [MASTER] +; disable= +; C0114, # missing-module-docstring +; C0115, # missing-class-docstring +; C0116, # missing-function-docstring +; C0209, # consider-using-f-string +; W0622, # redefined-builtin +; W1113, # too-many-arguments +; W0613, # unused-argument +; W0718, # broad-exception-caught +; W0511, # fixme +; W0621, # redefined-outer-name +; W1514, # unspecified-encoding +; W0603, # global-statement +; W1203, # logging-fstring-interpolation +; W0212, # protected-access +; C0301, # line-too-long +; W0707, # raise-missing-from - # TODO - W3101, # missing timeout on request - W0719, # broad-exception-raised +; # TODO +; W3101, # missing timeout on request +; W0719, # broad-exception-raised -[FORMAT] -max-line-length=120 +; [FORMAT] +; max-line-length=120 -[SIMILARITIES] -ignore-imports=yes +; [SIMILARITIES] +; ignore-imports=yes diff --git a/pylint_formatter.sh b/pylint_formatter.sh deleted file mode 100755 index 717b3b8..0000000 --- a/pylint_formatter.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# Get a list of changed files in the current branch compared to the main branch -FILES=$(git diff --name-only main | grep '\.py$') - -# Run Black on each Python file -echo "Formatting changed Python files..." -for file in $FILES; do - black $file -done - -echo "Formatting complete." diff --git a/pyproject.toml b/pyproject.toml index b1403fc..960aed6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ dependencies = [ [project.optional-dependencies] dev = [ + "pytest", "ruff", ] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 71b4008..0000000 --- a/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -typer>=0.9.0 -rich -GitPython -requests -pyyaml -typing-extensions>=4.7.0 -questionary -mixpanel -tomlkit -pathspec -httpx -packaging -charset-normalizer>=3.0.0 -websocket-client -psutil \ No newline at end of file diff --git a/tests/requirements.txt b/tests/requirements.txt deleted file mode 100644 index 55b033e..0000000 --- a/tests/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pytest \ No newline at end of file From e7300aa88c80acb8001a6a4f134aabcde69adde2 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 2 Aug 2024 14:13:42 -0400 Subject: [PATCH 03/10] `line-length=150` was too aggressive, change back to `120` --- .gitignore | 1 + pyproject.toml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 61e15be..12ab2f7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ __pycache__/ #COMMON CONFIGs .DS_Store +.ruff_cache .src_port .webpack_watch.log *.swp diff --git a/pyproject.toml b/pyproject.toml index 960aed6..803c095 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,9 +61,10 @@ where = ["."] include = ["comfy_cli*"] [tool.ruff] -line-length = 150 +line-length = 120 target-version = "py39" +[tool.ruff.lint] select = [ "E4", # default "E7", # default From 997dae58a99bc9052186a095db0416f9d5be54a6 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 16 Aug 2024 21:41:39 -0400 Subject: [PATCH 04/10] renamed `pylint.yml` -> `ruff_check.yml` in github actions --- .github/workflows/{pylint.yml => ruff_check.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{pylint.yml => ruff_check.yml} (100%) diff --git a/.github/workflows/pylint.yml b/.github/workflows/ruff_check.yml similarity index 100% rename from .github/workflows/pylint.yml rename to .github/workflows/ruff_check.yml From 814d274e36f5d605723d61dbf7ad8877c4ea2711 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 16 Aug 2024 21:42:15 -0400 Subject: [PATCH 05/10] implement `ruff_check` github action --- .github/workflows/ruff_check.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ruff_check.yml b/.github/workflows/ruff_check.yml index fcd91de..3a6b01e 100644 --- a/.github/workflows/ruff_check.yml +++ b/.github/workflows/ruff_check.yml @@ -1,4 +1,4 @@ -name: Lint +name: ruff_check on: push: @@ -9,7 +9,7 @@ on: - main jobs: - lint: + ruff_check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -17,10 +17,11 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.x" - - name: Install dependencies + - name: install ruff run: | python -m pip install --upgrade pip - pip install -r requirements.txt - pip install pylint - - name: Lint with Pylint - run: pylint --disable=R **/*.py + pip install ruff + - name: lint check and then format check with ruff + run: | + ruff lint + ruff format --check From 0f9ba3561b0523c4f37fac5ea208f09874159b07 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 16 Aug 2024 21:42:41 -0400 Subject: [PATCH 06/10] in `pre-commit` config, refactor all uses of `pylint` -> `ruff` --- .pre-commit-config.yaml | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index df05ef6..fe9a394 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,10 @@ repos: - - repo: https://github.com/psf/black - rev: 22.3.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.6.1 hooks: - - id: black - - repo: https://github.com/pylint-dev/pylint - rev: v2.16.2 - hooks: - - id: pylint - args: - # Note: E0401 is disabled as pylint does not run in an environment - # where the package is installed - - --disable=R,E0401 - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort + # Run the linter. + - id: ruff + args: [ --fix ] + # Run the formatter. + - id: ruff-format From 9b6ac6fcad10c142b29106066f1be886fbc37a1a Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 16 Aug 2024 21:49:40 -0400 Subject: [PATCH 07/10] fix ruff linting command in ruff GA --- .github/workflows/ruff_check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ruff_check.yml b/.github/workflows/ruff_check.yml index 3a6b01e..f6d2e2a 100644 --- a/.github/workflows/ruff_check.yml +++ b/.github/workflows/ruff_check.yml @@ -23,5 +23,5 @@ jobs: pip install ruff - name: lint check and then format check with ruff run: | - ruff lint + ruff check ruff format --check From 93b570c69bb24ea47b031f6ccb19263b4e7d44b3 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 16 Aug 2024 22:13:58 -0400 Subject: [PATCH 08/10] add `pre-commit` to dev dependencies --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 803c095..83047b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,7 @@ dependencies = [ [project.optional-dependencies] dev = [ + "pre-commit", "pytest", "ruff", ] From 0a6c10453b4552ac6f24bf63679fe9449abd6a51 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 16 Aug 2024 22:16:48 -0400 Subject: [PATCH 09/10] initial apply of ruff lint/format via `pre-commit run --all-files` --- comfy_cli/cmdline.py | 87 ++++--------- .../custom_nodes/bisect_custom_nodes.py | 16 +-- comfy_cli/command/custom_nodes/cm_cli_util.py | 17 +-- comfy_cli/command/custom_nodes/command.py | 115 +++++------------- comfy_cli/command/install.py | 40 ++---- comfy_cli/command/launch.py | 16 +-- comfy_cli/command/models/models.py | 70 +++-------- comfy_cli/command/run.py | 58 +++------ comfy_cli/config_manager.py | 18 +-- comfy_cli/constants.py | 4 +- comfy_cli/env_checker.py | 18 +-- comfy_cli/file_utils.py | 20 +-- comfy_cli/registry/api.py | 50 ++------ comfy_cli/registry/config_parser.py | 31 ++--- comfy_cli/registry/types.py | 3 + comfy_cli/tracking.py | 26 +--- comfy_cli/ui.py | 33 ++--- comfy_cli/update.py | 4 +- comfy_cli/uv.py | 39 ++---- comfy_cli/workspace_manager.py | 49 ++------ tests/comfy_cli/command/test_command.py | 4 +- tests/comfy_cli/registry/test_api.py | 10 +- .../comfy_cli/registry/test_config_parser.py | 50 ++++---- tests/e2e/test_e2e.py | 4 +- tests/uv/test_uv.py | 22 ++-- 25 files changed, 228 insertions(+), 576 deletions(-) diff --git a/comfy_cli/cmdline.py b/comfy_cli/cmdline.py index e13dc68..8f98e78 100644 --- a/comfy_cli/cmdline.py +++ b/comfy_cli/cmdline.py @@ -9,7 +9,8 @@ from rich import print from rich.console import Console from typing_extensions import Annotated, List -from comfy_cli import constants, env_checker, logging, tracking, utils, ui + +from comfy_cli import constants, env_checker, logging, tracking, ui, utils from comfy_cli.command import custom_nodes from comfy_cli.command import install as install_inner from comfy_cli.command import run as run_inner @@ -44,9 +45,7 @@ def validate(self, _ctx: typer.Context, param: typer.CallbackParam, value: str): if value is not None and param.name not in self.group: self.group.append(param.name) if len(self.group) > 1: - raise typer.BadParameter( - f"option `{param.name}` is mutually exclusive with option `{self.group.pop()}`" - ) + raise typer.BadParameter(f"option `{param.name}` is mutually exclusive with option `{self.group.pop()}`") return value @@ -123,9 +122,7 @@ def entry( tracking.prompt_tracking_consent(skip_prompt, default_value=enable_telemetry) if ctx.invoked_subcommand is None: - print( - "[bold yellow]Welcome to Comfy CLI![/bold yellow]: https://github.com/Comfy-Org/comfy-cli" - ) + print("[bold yellow]Welcome to Comfy CLI![/bold yellow]: https://github.com/Comfy-Org/comfy-cli") print(ctx.get_help()) ctx.exit() @@ -180,9 +177,7 @@ def install( callback=g_gpu_exclusivity.validate, ), ] = None, - cuda_version: Annotated[ - CUDAVersion, typer.Option(show_default=True) - ] = CUDAVersion.v12_1, + cuda_version: Annotated[CUDAVersion, typer.Option(show_default=True)] = CUDAVersion.v12_1, amd: Annotated[ Optional[bool], typer.Option( @@ -216,9 +211,7 @@ def install( callback=g_gpu_exclusivity.validate, ), ] = None, - commit: Annotated[ - Optional[str], typer.Option(help="Specify commit hash for ComfyUI") - ] = None, + commit: Annotated[Optional[str], typer.Option(help="Specify commit hash for ComfyUI")] = None, fast_deps: Annotated[ Optional[bool], typer.Option( @@ -235,9 +228,7 @@ def install( is_comfy_installed_at_path, repo_dir = check_comfy_repo(comfy_path) if is_comfy_installed_at_path and not restore: - print( - f"[bold red]ComfyUI is already installed at the specified path:[/bold red] {comfy_path}\n" - ) + print(f"[bold red]ComfyUI is already installed at the specified path:[/bold red] {comfy_path}\n") print( "[bold yellow]If you want to restore dependencies, add the '--restore' option.[/bold yellow]", ) @@ -247,12 +238,8 @@ def install( comfy_path = str(repo_dir.working_dir) if checker.python_version.major < 3 or checker.python_version.minor < 9: - print( - "[bold red]Python version 3.9 or higher is required to run ComfyUI.[/bold red]" - ) - print( - f"You are currently using Python version {env_checker.format_python_version(checker.python_version)}." - ) + print("[bold red]Python version 3.9 or higher is required to run ComfyUI.[/bold red]") + print(f"You are currently using Python version {env_checker.format_python_version(checker.python_version)}.") platform = utils.get_os() if cpu: print("[bold yellow]Installing for CPU[/bold yellow]") @@ -274,9 +261,7 @@ def install( return None if nvidia and platform == constants.OS.MACOS: - print( - "[bold red]Nvidia GPU is never on MacOS. What are you smoking? 🤔[/bold red]" - ) + print("[bold red]Nvidia GPU is never on MacOS. What are you smoking? 🤔[/bold red]") raise typer.Exit(code=1) if platform != constants.OS.MACOS and m_series: @@ -305,14 +290,10 @@ def install( ) if gpu == GPU_OPTION.INTEL_ARC: - print( - "[bold yellow]Installing on Intel ARC is not yet completely supported[/bold yellow]" - ) + print("[bold yellow]Installing on Intel ARC is not yet completely supported[/bold yellow]") env_check = env_checker.EnvChecker() if env_check.conda_env is None: - print( - "[bold red]Intel ARC support requires conda environment to be activated.[/bold red]" - ) + print("[bold red]Intel ARC support requires conda environment to be activated.[/bold red]") raise typer.Exit(code=1) if intel_arc is None: confirm_result = ui.prompt_confirm_action( @@ -381,9 +362,7 @@ def update( custom_nodes.command.update_node_id_cache() -@app.command( - help="Run API workflow file using the ComfyUI launched by `comfy launch --background`" -) +@app.command(help="Run API workflow file using the ComfyUI launched by `comfy launch --background`") @tracking.track_command() def run( workflow: Annotated[str, typer.Option(help="Path to the workflow API json file.")], @@ -397,9 +376,7 @@ def run( ] = False, host: Annotated[ Optional[str], - typer.Option( - help="The IP/hostname where the ComfyUI instance is running, e.g. 127.0.0.1 or localhost." - ), + typer.Option(help="The IP/hostname where the ComfyUI instance is running, e.g. 127.0.0.1 or localhost."), ] = None, port: Annotated[ Optional[int], @@ -438,9 +415,7 @@ def run( def validate_comfyui(_env_checker): if _env_checker.comfy_repo is None: - print( - "[bold red]If ComfyUI is not installed, this feature cannot be used.[/bold red]" - ) + print("[bold red]If ComfyUI is not installed, this feature cannot be used.[/bold red]") raise typer.Exit(code=1) @@ -460,9 +435,7 @@ def stop(): if not is_killed: print("[bold red]Failed to stop ComfyUI in the background.[/bold red]\n") else: - print( - f"[bold yellow]Background ComfyUI is stopped.[/bold yellow] ({bg_info[0]}:{bg_info[1]})" - ) + print(f"[bold yellow]Background ComfyUI is stopped.[/bold yellow] ({bg_info[0]}:{bg_info[1]})") ConfigManager().remove_background() @@ -470,9 +443,7 @@ def stop(): @app.command(help="Launch ComfyUI: ?[--background] ?[-- ]") @tracking.track_command() def launch( - background: Annotated[ - bool, typer.Option(help="Launch ComfyUI in background") - ] = False, + background: Annotated[bool, typer.Option(help="Launch ComfyUI in background")] = False, extra: List[str] = typer.Argument(None), ): launch_command(background, extra) @@ -482,9 +453,7 @@ def launch( @tracking.track_command() def set_default( workspace_path: str, - launch_extras: Annotated[ - str, typer.Option(help="Specify extra options for launch") - ] = "", + launch_extras: Annotated[str, typer.Option(help="Specify extra options for launch")] = "", ): comfy_path = os.path.abspath(os.path.expanduser(workspace_path)) @@ -536,17 +505,13 @@ def env(): @app.command(hidden=True) @tracking.track_command() def nodes(): - print( - "\n[bold red] No such command, did you mean 'comfy node' instead?[/bold red]\n" - ) + print("\n[bold red] No such command, did you mean 'comfy node' instead?[/bold red]\n") @app.command(hidden=True) @tracking.track_command() def models(): - print( - "\n[bold red] No such command, did you mean 'comfy model' instead?[/bold red]\n" - ) + print("\n[bold red] No such command, did you mean 'comfy model' instead?[/bold red]\n") @app.command(help="Provide feedback on the Comfy CLI tool.") @@ -560,9 +525,7 @@ def feedback(): choices=["1", "2", "3", "4", "5"], force_prompting=True, ) - tracking.track_event( - "feedback_general_satisfaction", {"score": general_satisfaction_score} - ) + tracking.track_event("feedback_general_satisfaction", {"score": general_satisfaction_score}) # Usability and User Experience usability_satisfaction_score = ui.prompt_select( @@ -570,14 +533,10 @@ def feedback(): choices=["1", "2", "3", "4", "5"], force_prompting=True, ) - tracking.track_event( - "feedback_usability_satisfaction", {"score": usability_satisfaction_score} - ) + tracking.track_event("feedback_usability_satisfaction", {"score": usability_satisfaction_score}) # Additional Feature-Specific Feedback - if questionary.confirm( - "Do you want to provide additional feature-specific feedback on our GitHub page?" - ).ask(): + if questionary.confirm("Do you want to provide additional feature-specific feedback on our GitHub page?").ask(): tracking.track_event("feedback_additional") webbrowser.open("https://github.com/Comfy-Org/comfy-cli/issues/new/choose") diff --git a/comfy_cli/command/custom_nodes/bisect_custom_nodes.py b/comfy_cli/command/custom_nodes/bisect_custom_nodes.py index c698247..b0da6dc 100644 --- a/comfy_cli/command/custom_nodes/bisect_custom_nodes.py +++ b/comfy_cli/command/custom_nodes/bisect_custom_nodes.py @@ -115,9 +115,7 @@ def set_custom_node_enabled_states(self): execute_cm_cli(["disable", *self.inactive_nodes]) def __str__(self): - active_list = "\n".join( - [f"{i + 1:3}. {node}" for i, node in enumerate(self.active)] - ) + active_list = "\n".join([f"{i + 1:3}. {node}" for i, node in enumerate(self.active)]) return f"""BisectState(status={self.status}) set of nodes with culprit: {len(self.range)} set of nodes to test: {len(self.active)} @@ -131,9 +129,7 @@ def __str__(self): + "?[-- ]" ) def start( - pinned_nodes: Annotated[ - str, typer.Option(help="Pinned nodes always enable during the bisect") - ] = "", + pinned_nodes: Annotated[str, typer.Option(help="Pinned nodes always enable during the bisect")] = "", extra: list[str] = typer.Argument(None), ): """Start a new bisect session. The initial state is bad with all custom nodes @@ -171,9 +167,7 @@ def start( bad() -@bisect_app.command( - help="Mark the current active set as good, indicating the problem is outside the test set." -) +@bisect_app.command(help="Mark the current active set as good, indicating the problem is outside the test set.") def good(): state = BisectState.load() if state.status != "running": @@ -192,9 +186,7 @@ def good(): launch_command(background=False, extra=state.launch_args) -@bisect_app.command( - help="Mark the current active set as bad, indicating the problem is within the test set." -) +@bisect_app.command(help="Mark the current active set as bad, indicating the problem is within the test set.") def bad(): state = BisectState.load() if state.status != "running": diff --git a/comfy_cli/command/custom_nodes/cm_cli_util.py b/comfy_cli/command/custom_nodes/cm_cli_util.py index c17c944..1e48652 100644 --- a/comfy_cli/command/custom_nodes/cm_cli_util.py +++ b/comfy_cli/command/custom_nodes/cm_cli_util.py @@ -16,10 +16,11 @@ # set of commands that invalidate (ie require an update of) dependencies after they are run _dependency_cmds = { - 'install', - 'reinstall', + "install", + "reinstall", } + def execute_cm_cli(args, channel=None, fast_deps=False, mode=None) -> str | None: _config_manager = ConfigManager() @@ -29,9 +30,7 @@ def execute_cm_cli(args, channel=None, fast_deps=False, mode=None) -> str | None print("\n[bold red]ComfyUI path is not resolved.[/bold red]\n", file=sys.stderr) raise typer.Exit(code=1) - cm_cli_path = os.path.join( - workspace_path, "custom_nodes", "ComfyUI-Manager", "cm-cli.py" - ) + cm_cli_path = os.path.join(workspace_path, "custom_nodes", "ComfyUI-Manager", "cm-cli.py") if not os.path.exists(cm_cli_path): print( f"\n[bold red]ComfyUI-Manager not found: {cm_cli_path}[/bold red]\n", @@ -51,18 +50,14 @@ def execute_cm_cli(args, channel=None, fast_deps=False, mode=None) -> str | None cmd += ["--mode", mode] new_env = os.environ.copy() - session_path = os.path.join( - _config_manager.get_config_path(), "tmp", str(uuid.uuid4()) - ) + session_path = os.path.join(_config_manager.get_config_path(), "tmp", str(uuid.uuid4())) new_env["__COMFY_CLI_SESSION__"] = session_path new_env["COMFYUI_PATH"] = workspace_path print(f"Execute from: {workspace_path}") try: - result = subprocess.run( - cmd, env=new_env, check=True, capture_output=True, text=True - ) + result = subprocess.run(cmd, env=new_env, check=True, capture_output=True, text=True) print(result.stdout) if fast_deps and args[0] in _dependency_cmds: diff --git a/comfy_cli/command/custom_nodes/command.py b/comfy_cli/command/custom_nodes/command.py index 823a1a2..50b4ef6 100644 --- a/comfy_cli/command/custom_nodes/command.py +++ b/comfy_cli/command/custom_nodes/command.py @@ -39,9 +39,7 @@ def validate_comfyui_manager(_env_checker): manager_path = _env_checker.get_comfyui_manager_path() if manager_path is None: - print( - "[bold red]If ComfyUI is not installed, this feature cannot be used.[/bold red]" - ) + print("[bold red]If ComfyUI is not installed, this feature cannot be used.[/bold red]") raise typer.Exit(code=1) elif not os.path.exists(manager_path): print( @@ -73,9 +71,7 @@ def get_installed_packages(): if pip_map is None: try: - result = subprocess.check_output( - [sys.executable, "-m", "pip", "list"], universal_newlines=True - ) + result = subprocess.check_output([sys.executable, "-m", "pip", "list"], universal_newlines=True) pip_map = {} for line in result.split("\n"): @@ -87,23 +83,18 @@ def get_installed_packages(): pip_map[y[0]] = y[1] except subprocess.CalledProcessError: - print( - "[ComfyUI-Manager] Failed to retrieve the information of installed pip packages." - ) + print("[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.") return set() return pip_map def try_install_script(repo_path, install_cmd, instant_execution=False): - startup_script_path = os.path.join( - workspace_manager.workspace_path, "startup-scripts" - ) + startup_script_path = os.path.join(workspace_manager.workspace_path, "startup-scripts") if not instant_execution and ( (len(install_cmd) > 0 and install_cmd[0].startswith("#")) or ( - platform.system() - == "Windows" + platform.system() == "Windows" # From Yoland: disable commit compare # and comfy_ui_commit_datetime.date() # >= comfy_ui_required_commit_datetime.date() @@ -179,9 +170,7 @@ def execute_install_script(repo_path): def save_snapshot( output: Annotated[ Optional[str], - typer.Option( - show_default=False, help="Specify the output file path. (.json/.yaml)" - ), + typer.Option(show_default=False, help="Specify the output file path. (.json/.yaml)"), ] = None, ): if output is None: @@ -229,9 +218,7 @@ def restore_snapshot( execute_cm_cli(["restore-snapshot", path] + extras) -@app.command( - "restore-dependencies", help="Restore dependencies from installed custom nodes" -) +@app.command("restore-dependencies", help="Restore dependencies from installed custom nodes") @tracking.track_command("node") def restore_dependencies(): execute_cm_cli(["restore-dependencies"]) @@ -272,24 +259,16 @@ def clear(): mode_completer = utils.create_choice_completer(["remote", "local", "cache"]) -channel_completer = utils.create_choice_completer( - ["default", "recent", "dev", "forked", "tutorial", "legacy"] -) +channel_completer = utils.create_choice_completer(["default", "recent", "dev", "forked", "tutorial", "legacy"]) def node_completer(incomplete: str) -> list[str]: try: config_manager = ConfigManager() - tmp_path = os.path.join( - config_manager.get_config_path(), "tmp", "node-cache.list" - ) + tmp_path = os.path.join(config_manager.get_config_path(), "tmp", "node-cache.list") with open(tmp_path, "r", encoding="UTF-8", errors="ignore") as cache_file: - return [ - node_id - for node_id in cache_file.readlines() - if node_id.startswith(incomplete) - ] + return [node_id for node_id in cache_file.readlines() if node_id.startswith(incomplete)] except Exception: return [] @@ -298,20 +277,14 @@ def node_completer(incomplete: str) -> list[str]: def node_or_all_completer(incomplete: str) -> list[str]: try: config_manager = ConfigManager() - tmp_path = os.path.join( - config_manager.get_config_path(), "tmp", "node-cache.list" - ) + tmp_path = os.path.join(config_manager.get_config_path(), "tmp", "node-cache.list") all_opt = [] if "all".startswith(incomplete): all_opt = ["all"] with open(tmp_path, "r", encoding="UTF-8", errors="ignore") as cache_file: - return [ - node_id - for node_id in cache_file.readlines() - if node_id.startswith(incomplete) - ] + all_opt + return [node_id for node_id in cache_file.readlines() if node_id.startswith(incomplete)] + all_opt except Exception: return [] @@ -409,9 +382,7 @@ def simple_show( @app.command(help="Install custom nodes") @tracking.track_command("node") def install( - nodes: List[str] = typer.Argument( - ..., help="List of custom nodes to install", autocompletion=node_completer - ), + nodes: List[str] = typer.Argument(..., help="List of custom nodes to install", autocompletion=node_completer), channel: Annotated[ Optional[str], typer.Option( @@ -446,9 +417,7 @@ def install( @app.command(help="Reinstall custom nodes") @tracking.track_command("node") def reinstall( - nodes: List[str] = typer.Argument( - ..., help="List of custom nodes to reinstall", autocompletion=node_completer - ), + nodes: List[str] = typer.Argument(..., help="List of custom nodes to reinstall", autocompletion=node_completer), channel: Annotated[ Optional[str], typer.Option( @@ -483,9 +452,7 @@ def reinstall( @app.command(help="Uninstall custom nodes") @tracking.track_command("node") def uninstall( - nodes: List[str] = typer.Argument( - ..., help="List of custom nodes to uninstall", autocompletion=node_completer - ), + nodes: List[str] = typer.Argument(..., help="List of custom nodes to uninstall", autocompletion=node_completer), channel: Annotated[ Optional[str], typer.Option( @@ -513,9 +480,7 @@ def update_node_id_cache(): config_manager = ConfigManager() workspace_path = workspace_manager.workspace_path - cm_cli_path = os.path.join( - workspace_path, "custom_nodes", "ComfyUI-Manager", "cm-cli.py" - ) + cm_cli_path = os.path.join(workspace_path, "custom_nodes", "ComfyUI-Manager", "cm-cli.py") tmp_path = os.path.join(config_manager.get_config_path(), "tmp") if not os.path.exists(tmp_path): @@ -677,16 +642,12 @@ def install_deps( validate_mode(mode) if deps is None and workflow is None: - print( - "[bold red]One of --deps or --workflow must be provided as an argument.[/bold red]\n" - ) + print("[bold red]One of --deps or --workflow must be provided as an argument.[/bold red]\n") tmp_path = None if workflow is not None: workflow = os.path.abspath(os.path.expanduser(workflow)) - tmp_path = os.path.join( - workspace_manager.config_manager.get_config_path(), "tmp" - ) + tmp_path = os.path.join(workspace_manager.config_manager.get_config_path(), "tmp") if not os.path.exists(tmp_path): os.makedirs(tmp_path) tmp_path = os.path.join(tmp_path, str(uuid.uuid4())) + ".json" @@ -707,17 +668,11 @@ def install_deps( os.remove(tmp_path) -@app.command( - "deps-in-workflow", help="Generate dependencies file from workflow (.json/.png)" -) +@app.command("deps-in-workflow", help="Generate dependencies file from workflow (.json/.png)") @tracking.track_command("node") def deps_in_workflow( - workflow: Annotated[ - str, typer.Option(show_default=False, help="Workflow file (.json/.png)") - ], - output: Annotated[ - str, typer.Option(show_default=False, help="Output file (.json)") - ], + workflow: Annotated[str, typer.Option(show_default=False, help="Workflow file (.json/.png)")], + output: Annotated[str, typer.Option(show_default=False, help="Output file (.json)")], channel: Annotated[ Optional[str], typer.Option( @@ -747,9 +702,7 @@ def deps_in_workflow( @app.command("publish", help="Publish node to registry") @tracking.track_command("publish") def publish( - token: Optional[str] = typer.Option( - None, "--token", help="Personal Access Token for publishing", hide_input=True - ) + token: Optional[str] = typer.Option(None, "--token", help="Personal Access Token for publishing", hide_input=True), ): """ Publish a node with optional validation. @@ -793,9 +746,7 @@ def scaffold(): typer.echo("Initializing metadata...") initialize_project_config() - typer.echo( - "pyproject.toml created successfully. Defaults were filled in. Please check before publishing." - ) + typer.echo("pyproject.toml created successfully. Defaults were filled in. Please check before publishing.") @app.command("registry-list", help="List all nodes in the registry", hidden=True) @@ -878,9 +829,7 @@ def registry_install( return except Exception as e: - logging.error( - f"Encountered an error while installing the node. error: {str(e)}" - ) + logging.error(f"Encountered an error while installing the node. error: {str(e)}") ui.display_error_message(f"Failed to download the custom node {node_id}.") return @@ -898,20 +847,14 @@ def registry_install( ) if not confirm: return - node_specific_path.mkdir( - parents=True, exist_ok=True - ) # Create the directory if it doesn't exist + node_specific_path.mkdir(parents=True, exist_ok=True) # Create the directory if it doesn't exist local_filename = node_specific_path / f"{node_id}-{node_version.version}.zip" - logging.debug( - f"Start downloading the node {node_id} version {node_version.version} to {local_filename}" - ) + logging.debug(f"Start downloading the node {node_id} version {node_version.version} to {local_filename}") download_file(node_version.download_url, local_filename) # Extract the downloaded archive to the custom_node directory on the workspace. - logging.debug( - f"Start extracting the node {node_id} version {node_version.version} to {custom_nodes_path}" - ) + logging.debug(f"Start extracting the node {node_id} version {node_version.version} to {custom_nodes_path}") extract_package_as_zip(local_filename, node_specific_path) # TODO: temoporary solution to run requirement.txt and install script @@ -921,9 +864,7 @@ def registry_install( logging.debug(f"Deleting the downloaded archive {local_filename}") os.remove(local_filename) - logging.info( - f"Node {node_id} version {node_version.version} has been successfully installed." - ) + logging.info(f"Node {node_id} version {node_version.version} has been successfully installed.") @app.command( diff --git a/comfy_cli/command/install.py b/comfy_cli/command/install.py index f4c8f0d..71306fc 100644 --- a/comfy_cli/command/install.py +++ b/comfy_cli/command/install.py @@ -1,11 +1,12 @@ -from rich import print import os import platform import subprocess import sys -import typer from typing import Optional +import typer +from rich import print + 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 @@ -61,18 +62,12 @@ def pip_install_comfyui_dependencies( "torchvision", "torchaudio", ] - if ( - plat == constants.OS.WINDOWS - and cuda_version == constants.CUDAVersion.v12_1 - ): + if plat == constants.OS.WINDOWS and cuda_version == constants.CUDAVersion.v12_1: base_command += [ "--extra-index-url", "https://download.pytorch.org/whl/cu121", ] - elif ( - plat == constants.OS.WINDOWS - and cuda_version == constants.CUDAVersion.v11_8 - ): + elif plat == constants.OS.WINDOWS and cuda_version == constants.CUDAVersion.v11_8: base_command += [ "--extra-index-url", "https://download.pytorch.org/whl/cu118", @@ -108,16 +103,12 @@ def pip_install_comfyui_dependencies( check=False, ) if result and result.returncode != 0: - print( - "Failed to install PyTorch dependencies. Please check your environment (`comfy env`) and try again" - ) + print("Failed to install PyTorch dependencies. Please check your environment (`comfy env`) and try again") sys.exit(1) # install directml for AMD windows if gpu == GPU_OPTION.AMD and plat == constants.OS.WINDOWS: - result = subprocess.run( - [sys.executable, "-m", "pip", "install", "torch-directml"], check=True - ) + result = subprocess.run([sys.executable, "-m", "pip", "install", "torch-directml"], check=True) # install torch for Mac M Series if gpu == GPU_OPTION.M_SERIES: @@ -140,22 +131,16 @@ def pip_install_comfyui_dependencies( # install requirements.txt if skip_requirement: return - result = subprocess.run( - [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], check=False - ) + result = subprocess.run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], check=False) if result.returncode != 0: - print( - "Failed to install ComfyUI dependencies. Please check your environment (`comfy env`) and try again." - ) + print("Failed to install ComfyUI dependencies. Please check your environment (`comfy env`) and try again.") sys.exit(1) # install requirements for manager 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 - ) + subprocess.run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], check=True) def execute( @@ -174,7 +159,6 @@ def execute( *args, **kwargs, ): - if not workspace_manager.skip_prompting: res = ui.prompt_confirm_action(f"Install from {url} to {comfy_path}?", True) @@ -210,9 +194,7 @@ def execute( subprocess.run(["git", "checkout", commit], check=True) if not fast_deps: - pip_install_comfyui_dependencies( - repo_dir, gpu, plat, cuda_version, skip_torch_or_directml, skip_requirement - ) + 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) diff --git a/comfy_cli/command/launch.py b/comfy_cli/command/launch.py index 7b37410..5d563c1 100644 --- a/comfy_cli/command/launch.py +++ b/comfy_cli/command/launch.py @@ -27,9 +27,7 @@ def launch_comfyui(extra): new_env = os.environ.copy() - session_path = os.path.join( - ConfigManager().get_config_path(), "tmp", str(uuid.uuid4()) - ) + session_path = os.path.join(ConfigManager().get_config_path(), "tmp", str(uuid.uuid4())) new_env["__COMFY_CLI_SESSION__"] = session_path new_env["PYTHONENCODING"] = "utf-8" @@ -43,9 +41,7 @@ def launch_comfyui(extra): if "COMFY_CLI_BACKGROUND" not in os.environ: # If not running in background mode, there's no need to use popen. This can prevent the issue of linefeeds occurring with tqdm. while True: - res = subprocess.run( - [sys.executable, "main.py"] + extra, env=new_env, check=False - ) + res = subprocess.run([sys.executable, "main.py"] + extra, env=new_env, check=False) if reboot_path is None: print("[bold red]ComfyUI is not installed.[/bold red]\n") @@ -122,9 +118,7 @@ def launch( ) raise typer.Exit(code=1) - if ( - extra is None or len(extra) == 0 - ) and workspace_manager.workspace_type == WorkspaceType.DEFAULT: + if (extra is None or len(extra) == 0) and workspace_manager.workspace_type == WorkspaceType.DEFAULT: launch_extras = workspace_manager.config_manager.config["DEFAULT"].get( constants.CONFIG_KEY_DEFAULT_LAUNCH_EXTRAS, "" ) @@ -244,9 +238,7 @@ def msg_hook(stream): print( f"[bold yellow]ComfyUI is successfully launched in the background.[/bold yellow]\nTo see the GUI go to: http://{listen}:{port}" ) - ConfigManager().config["DEFAULT"][ - constants.CONFIG_KEY_BACKGROUND - ] = f"{(listen, port, process.pid)}" + ConfigManager().config["DEFAULT"][constants.CONFIG_KEY_BACKGROUND] = f"{(listen, port, process.pid)}" ConfigManager().write_config() # NOTE: os.exit(0) doesn't work. diff --git a/comfy_cli/command/models/models.py b/comfy_cli/command/models/models.py index 9d27bfc..82740cb 100644 --- a/comfy_cli/command/models/models.py +++ b/comfy_cli/command/models/models.py @@ -97,13 +97,9 @@ def request_civitai_model_version_api(version_id: int, headers: Optional[dict] = return model_name, download_url, model_type, basemodel -def request_civitai_model_api( - model_id: int, version_id: int = None, headers: Optional[dict] = None -): +def request_civitai_model_api(model_id: int, version_id: int = None, headers: Optional[dict] = None): # Make a request to the Civitai API to get the model information - response = requests.get( - f"https://civitai.com/api/v1/models/{model_id}", headers=headers, timeout=10 - ) + response = requests.get(f"https://civitai.com/api/v1/models/{model_id}", headers=headers, timeout=10) response.raise_for_status() # Raise an error for bad status codes model_data = response.json() @@ -134,9 +130,7 @@ def download( _ctx: typer.Context, url: Annotated[ str, - typer.Option( - help="The URL from which to download the model", show_default=False - ), + typer.Option(help="The URL from which to download the model", show_default=False), ], relative_path: Annotated[ Optional[str], @@ -181,55 +175,35 @@ def download( "Authorization": f"Bearer {civitai_api_token}", } - is_civitai_model_url, is_civitai_api_url, model_id, version_id = check_civitai_url( - url - ) + is_civitai_model_url, is_civitai_api_url, model_id, version_id = check_civitai_url(url) if is_civitai_model_url: - local_filename, url, model_type, basemodel = request_civitai_model_api( - model_id, version_id, headers - ) + local_filename, url, model_type, basemodel = request_civitai_model_api(model_id, version_id, headers) model_path = model_path_map.get(model_type) if relative_path is None: if model_path is None: - model_path = ui.prompt_input( - "Enter model type path (e.g. loras, checkpoints, ...)", default="" - ) + model_path = ui.prompt_input("Enter model type path (e.g. loras, checkpoints, ...)", default="") - relative_path = os.path.join( - DEFAULT_COMFY_MODEL_PATH, model_path, basemodel - ) + relative_path = os.path.join(DEFAULT_COMFY_MODEL_PATH, model_path, basemodel) elif is_civitai_api_url: - local_filename, url, model_type, basemodel = request_civitai_model_version_api( - version_id, headers - ) + local_filename, url, model_type, basemodel = request_civitai_model_version_api(version_id, headers) model_path = model_path_map.get(model_type) if relative_path is None: if model_path is None: - model_path = ui.prompt_input( - "Enter model type path (e.g. loras, checkpoints, ...)", default="" - ) + model_path = ui.prompt_input("Enter model type path (e.g. loras, checkpoints, ...)", default="") - relative_path = os.path.join( - DEFAULT_COMFY_MODEL_PATH, model_path, basemodel - ) + relative_path = os.path.join(DEFAULT_COMFY_MODEL_PATH, model_path, basemodel) elif check_huggingface_url(url): local_filename = potentially_strip_param_url(url.split("/")[-1]) if relative_path is None: - model_path = ui.prompt_input( - "Enter model type path (e.g. loras, checkpoints, ...)", default="" - ) - basemodel = ui.prompt_input( - "Enter base model (e.g. SD1.5, SDXL, ...)", default="" - ) - relative_path = os.path.join( - DEFAULT_COMFY_MODEL_PATH, model_path, basemodel - ) + model_path = ui.prompt_input("Enter model type path (e.g. loras, checkpoints, ...)", default="") + basemodel = ui.prompt_input("Enter base model (e.g. SD1.5, SDXL, ...)", default="") + relative_path = os.path.join(DEFAULT_COMFY_MODEL_PATH, model_path, basemodel) else: print("Model source is unknown") @@ -237,9 +211,7 @@ def download( if local_filename is None: local_filename = ui.prompt_input("Enter filename to save model as") else: - local_filename = ui.prompt_input( - "Enter filename to save model as", default=local_filename - ) + local_filename = ui.prompt_input("Enter filename to save model as", default=local_filename) else: local_filename = filename @@ -304,18 +276,13 @@ def remove( missing_models.append(name) if missing_models: - typer.echo( - "The following models were not found and cannot be removed: " - + ", ".join(missing_models) - ) + typer.echo("The following models were not found and cannot be removed: " + ", ".join(missing_models)) if not to_delete: return # Exit if no valid models were found # Scenario #2: User did not provide model names, prompt for selection else: - selections = ui.prompt_multi_select( - "Select models to delete:", [model.name for model in available_models] - ) + selections = ui.prompt_multi_select("Select models to delete:", [model.name for model in available_models]) if not selections: typer.echo("No models selected for deletion.") return @@ -323,10 +290,7 @@ def remove( # Confirm deletion if to_delete and ( - confirm - or ui.prompt_confirm_action( - "Are you sure you want to delete the selected files?", False - ) + confirm or ui.prompt_confirm_action("Are you sure you want to delete the selected files?", False) ): for model_path in to_delete: model_path.unlink() diff --git a/comfy_cli/command/run.py b/comfy_cli/command/run.py index 4f94a96..663db73 100644 --- a/comfy_cli/command/run.py +++ b/comfy_cli/command/run.py @@ -35,9 +35,7 @@ def load_api_workflow(file: str): return workflow -def execute( - workflow: str, host, port, wait=True, verbose=False, local_paths=False, timeout=30 -): +def execute(workflow: str, host, port, wait=True, verbose=False, local_paths=False, timeout=30): workflow_name = os.path.abspath(os.path.expanduser(workflow)) if not os.path.isfile(workflow): pprint( @@ -49,15 +47,11 @@ def execute( workflow = load_api_workflow(workflow) if not workflow: - pprint( - "[bold red]Specified workflow does not appear to be an API workflow json file[/bold red]" - ) + pprint("[bold red]Specified workflow does not appear to be an API workflow json file[/bold red]") raise typer.Exit(code=1) if not check_comfy_server_running(port, host): - pprint( - f"[bold red]ComfyUI not running on specified address ({host}:{port})[/bold red]" - ) + pprint(f"[bold red]ComfyUI not running on specified address ({host}:{port})[/bold red]") raise typer.Exit(code=1) progress = None @@ -69,9 +63,7 @@ def execute( else: print(f"Queuing workflow: {workflow_name}") - execution = WorkflowExecution( - workflow, host, port, verbose, progress, local_paths, timeout - ) + execution = WorkflowExecution(workflow, host, port, verbose, progress, local_paths, timeout) try: if wait: @@ -90,9 +82,7 @@ def execute( pprint(f) elapsed = timedelta(seconds=end - start) - pprint( - f"[bold green]\nWorkflow execution completed ({elapsed})[/bold green]" - ) + pprint(f"[bold green]\nWorkflow execution completed ({elapsed})[/bold green]") else: pprint("[bold green]Workflow queued[/bold green]") finally: @@ -103,32 +93,22 @@ def execute( class ExecutionProgress(Progress): def get_renderables(self): table_columns = ( - ( - Column(no_wrap=True) - if isinstance(_column, str) - else _column.get_table_column().copy() - ) + (Column(no_wrap=True) if isinstance(_column, str) else _column.get_table_column().copy()) for _column in self.columns ) for task in self.tasks: percent = "[progress.percentage]{task.percentage:>3.0f}%".format(task=task) if task.fields.get("progress_type") == "overall": - overall_table = Table.grid( - *table_columns, padding=(0, 1), expand=self.expand - ) - overall_table.add_row( - BarColumn().render(task), percent, TimeElapsedColumn().render(task) - ) + overall_table = Table.grid(*table_columns, padding=(0, 1), expand=self.expand) + overall_table.add_row(BarColumn().render(task), percent, TimeElapsedColumn().render(task)) yield overall_table else: yield self.make_tasks_table([task]) class WorkflowExecution: - def __init__( - self, workflow, host, port, verbose, progress, local_paths, timeout=30 - ): + def __init__(self, workflow, host, port, verbose, progress, local_paths, timeout=30): self.workflow = workflow self.host = host self.port = port @@ -140,9 +120,7 @@ def __init__( self.remaining_nodes = set(self.workflow.keys()) self.total_nodes = len(self.remaining_nodes) if progress: - self.overall_task = self.progress.add_task( - "", total=self.total_nodes, progress_type="overall" - ) + self.overall_task = self.progress.add_task("", total=self.total_nodes, progress_type="overall") self.current_node = None self.progress_task = None self.progress_node = None @@ -156,9 +134,7 @@ def connect(self): def queue(self): data = {"prompt": self.workflow, "client_id": self.client_id} - req = request.Request( - f"http://{self.host}:{self.port}/prompt", json.dumps(data).encode("utf-8") - ) + req = request.Request(f"http://{self.host}:{self.port}/prompt", json.dumps(data).encode("utf-8")) try: resp = request.urlopen(req) body = json.loads(resp.read()) @@ -190,9 +166,7 @@ def watch_execution(self): break def update_overall_progress(self): - self.progress.update( - self.overall_task, completed=self.total_nodes - len(self.remaining_nodes) - ) + self.progress.update(self.overall_task, completed=self.total_nodes - len(self.remaining_nodes)) def get_node_title(self, node_id): node = self.workflow[node_id] @@ -223,9 +197,7 @@ def format_image_path(self, img): if subfolder: filename = os.path.join(subfolder, filename) - filename = os.path.join( - workspace_manager.get_workspace_path()[0], output_type, filename - ) + filename = os.path.join(workspace_manager.get_workspace_path()[0], output_type, filename) return filename query = urllib.parse.urlencode(img) @@ -300,7 +272,5 @@ def on_executed(self, data): self.outputs.append(self.format_image_path(img)) def on_error(self, data): - pprint( - f"[bold red]Error running workflow\n{json.dumps(data, indent=2)}[/bold red]" - ) + pprint(f"[bold red]Error running workflow\n{json.dumps(data, indent=2)}[/bold red]") raise typer.Exit(code=1) diff --git a/comfy_cli/config_manager.py b/comfy_cli/config_manager.py index 191e6a0..616bf09 100644 --- a/comfy_cli/config_manager.py +++ b/comfy_cli/config_manager.py @@ -41,9 +41,7 @@ def get(self, key): """ Get a value from the config file. Returns None if the key does not exist. """ - return self.config["DEFAULT"].get( - key, None - ) # Returns None if the key does not exist + return self.config["DEFAULT"].get(key, None) # Returns None if the key does not exist def load(self): config_file_path = self.get_config_file_path() @@ -74,9 +72,7 @@ def fill_print_env(self, table): self.config["DEFAULT"][constants.CONFIG_KEY_DEFAULT_WORKSPACE], ) - launch_extras = self.config["DEFAULT"].get( - constants.CONFIG_KEY_DEFAULT_LAUNCH_EXTRAS, "" - ) + launch_extras = self.config["DEFAULT"].get(constants.CONFIG_KEY_DEFAULT_LAUNCH_EXTRAS, "") else: table.add_row("Default ComfyUI workspace", "No default ComfyUI workspace") @@ -96,11 +92,7 @@ def fill_print_env(self, table): if self.config.has_option("DEFAULT", "enable_tracking"): table.add_row( "Tracking Analytics", - ( - "Enabled" - if self.config["DEFAULT"]["enable_tracking"] == "True" - else "Disabled" - ), + ("Enabled" if self.config["DEFAULT"]["enable_tracking"] == "True" else "Disabled"), ) if self.config.has_option("DEFAULT", constants.CONFIG_KEY_BACKGROUND): @@ -124,8 +116,6 @@ def get_cli_version(self): try: return version("comfy-cli") except Exception as e: - logging.debug( - f"Error occurred while retrieving CLI version using importlib.metadata: {e}" - ) + logging.debug(f"Error occurred while retrieving CLI version using importlib.metadata: {e}") return "0.0.0" diff --git a/comfy_cli/constants.py b/comfy_cli/constants.py index 11685cd..73f8870 100644 --- a/comfy_cli/constants.py +++ b/comfy_cli/constants.py @@ -20,9 +20,7 @@ class OS(Enum): DEFAULT_CONFIG = { OS.WINDOWS: os.path.join(os.path.expanduser("~"), "AppData", "Local", "comfy-cli"), - OS.MACOS: os.path.join( - os.path.expanduser("~"), "Library", "Application Support", "comfy-cli" - ), + OS.MACOS: os.path.join(os.path.expanduser("~"), "Library", "Application Support", "comfy-cli"), OS.LINUX: os.path.join(os.path.expanduser("~"), ".config", "comfy-cli"), } diff --git a/comfy_cli/env_checker.py b/comfy_cli/env_checker.py index 88422df..563d72a 100644 --- a/comfy_cli/env_checker.py +++ b/comfy_cli/env_checker.py @@ -29,12 +29,8 @@ def format_python_version(version_info): str: The formatted Python version string. """ if version_info.major == 3 and version_info.minor > 8: - return "{}.{}.{}".format( - version_info.major, version_info.minor, version_info.micro - ) - return "[bold red]{}.{}.{}[/bold red]".format( - version_info.major, version_info.minor, version_info.micro - ) + return "{}.{}.{}".format(version_info.major, version_info.minor, version_info.micro) + return "[bold red]{}.{}.{}[/bold red]".format(version_info.major, version_info.minor, version_info.micro) def check_comfy_server_running(port=8188, host="localhost"): @@ -90,14 +86,8 @@ def get_isolated_env(self): return None def check(self): - self.virtualenv_path = ( - os.environ.get("VIRTUAL_ENV") if os.environ.get("VIRTUAL_ENV") else None - ) - self.conda_env = ( - os.environ.get("CONDA_DEFAULT_ENV") - if os.environ.get("CONDA_DEFAULT_ENV") - else None - ) + self.virtualenv_path = os.environ.get("VIRTUAL_ENV") if os.environ.get("VIRTUAL_ENV") else None + self.conda_env = os.environ.get("CONDA_DEFAULT_ENV") if os.environ.get("CONDA_DEFAULT_ENV") else None # TODO: use ui.display_table def fill_print_table(self): diff --git a/comfy_cli/file_utils.py b/comfy_cli/file_utils.py index 73f852d..8f65b3c 100644 --- a/comfy_cli/file_utils.py +++ b/comfy_cli/file_utils.py @@ -46,13 +46,9 @@ def parse_json(input_data): return f"Unknown error occurred (status code: {status_code})" -def download_file( - url: str, local_filepath: pathlib.Path, headers: Optional[dict] = None -): +def download_file(url: str, local_filepath: pathlib.Path, headers: Optional[dict] = None): """Helper function to download a file.""" - local_filepath.parent.mkdir( - parents=True, exist_ok=True - ) # Ensure the directory exists + local_filepath.parent.mkdir(parents=True, exist_ok=True) # Ensure the directory exists with httpx.stream("GET", url, follow_redirects=True, headers=headers) as response: if response.status_code == 200: @@ -66,15 +62,11 @@ def download_file( ): f.write(data) except KeyboardInterrupt: - delete_eh = ui.prompt_confirm_action( - "Download interrupted, cleanup files?", True - ) + delete_eh = ui.prompt_confirm_action("Download interrupted, cleanup files?", True) if delete_eh: local_filepath.unlink() else: - status_reason = guess_status_code_reason( - response.status_code, response.read() - ) + status_reason = guess_status_code_reason(response.status_code, response.read()) raise DownloadException(f"Failed to download file.\n{status_reason}") @@ -114,9 +106,7 @@ def upload_file_to_signed_url(signed_url: str, file_path: str): print("Upload successful.") else: # Print a generic error message with status code and response text - print( - f"Upload failed with status code: {response.status_code}. Error: {response.text}" - ) + print(f"Upload failed with status code: {response.status_code}. Error: {response.text}") except requests.exceptions.RequestException as e: # Print error related to the HTTP request diff --git a/comfy_cli/registry/api.py b/comfy_cli/registry/api.py index 943e011..fe0375d 100644 --- a/comfy_cli/registry/api.py +++ b/comfy_cli/registry/api.py @@ -6,11 +6,11 @@ # Reduced global imports from comfy_cli.registry from comfy_cli.registry.types import ( + License, Node, NodeVersion, PublishNodeVersionResponse, PyProjectConfig, - License, ) @@ -27,9 +27,7 @@ def determine_base_url(self): else: return "https://api.comfy.org" - def publish_node_version( - self, node_config: PyProjectConfig, token - ) -> PublishNodeVersionResponse: + def publish_node_version(self, node_config: PyProjectConfig, token) -> PublishNodeVersionResponse: """ Publishes a new version of a node. @@ -42,14 +40,10 @@ def publish_node_version( """ # Local import to prevent circular dependency if not node_config.tool_comfy.publisher_id: - raise Exception( - "Publisher ID is required in pyproject.toml to publish a node version" - ) + raise Exception("Publisher ID is required in pyproject.toml to publish a node version") if not node_config.project.name: - raise Exception( - "Project name is required in pyproject.toml to publish a node version" - ) + raise Exception("Project name is required in pyproject.toml to publish a node version") license_json = serialize_license(node_config.project.license) request_body = { "personal_access_token": token, @@ -80,9 +74,7 @@ def publish_node_version( signedUrl=data["signedUrl"], ) else: - raise Exception( - f"Failed to publish node version: {response.status_code} {response.text}" - ) + raise Exception(f"Failed to publish node version: {response.status_code} {response.text}") def list_all_nodes(self): """ @@ -98,9 +90,7 @@ def list_all_nodes(self): mapped_nodes = [map_node_to_node_class(node) for node in raw_nodes] return mapped_nodes else: - raise Exception( - f"Failed to retrieve nodes: {response.status_code} - {response.text}" - ) + raise Exception(f"Failed to retrieve nodes: {response.status_code} - {response.text}") def install_node(self, node_id, version=None): """ @@ -124,9 +114,7 @@ def install_node(self, node_id, version=None): logging.debug(f"RegistryAPI install_node response: {response.json()}") return map_node_version(response.json()) else: - raise Exception( - f"Failed to install node: {response.status_code} - {response.text}" - ) + raise Exception(f"Failed to install node: {response.status_code} - {response.text}") def map_node_version(api_node_version): @@ -140,24 +128,14 @@ def map_node_version(api_node_version): NodeVersion: An instance of NodeVersion dataclass populated with data from the API. """ return NodeVersion( - changelog=api_node_version.get( - "changelog", "" - ), # Provide a default value if 'changelog' is missing + changelog=api_node_version.get("changelog", ""), # Provide a default value if 'changelog' is missing dependencies=api_node_version.get( "dependencies", [] ), # Provide a default empty list if 'dependencies' is missing - deprecated=api_node_version.get( - "deprecated", False - ), # Assume False if 'deprecated' is not specified - id=api_node_version[ - "id" - ], # 'id' should be mandatory; raise KeyError if missing - version=api_node_version[ - "version" - ], # 'version' should be mandatory; raise KeyError if missing - download_url=api_node_version.get( - "downloadUrl", "" - ), # Provide a default value if 'downloadUrl' is missing + deprecated=api_node_version.get("deprecated", False), # Assume False if 'deprecated' is not specified + id=api_node_version["id"], # 'id' should be mandatory; raise KeyError if missing + version=api_node_version["version"], # 'version' should be mandatory; raise KeyError if missing + download_url=api_node_version.get("downloadUrl", ""), # Provide a default value if 'downloadUrl' is missing ) @@ -181,9 +159,7 @@ def map_node_to_node_class(api_node_data): repository=api_node_data.get("repository"), tags=api_node_data.get("tags", []), latest_version=( - map_node_version(api_node_data["latest_version"]) - if "latest_version" in api_node_data - else None + map_node_version(api_node_data["latest_version"]) if "latest_version" in api_node_data else None ), ) diff --git a/comfy_cli/registry/config_parser.py b/comfy_cli/registry/config_parser.py index d61522b..2d4ca6d 100644 --- a/comfy_cli/registry/config_parser.py +++ b/comfy_cli/registry/config_parser.py @@ -1,18 +1,18 @@ import os import subprocess from typing import Optional -import typer import tomlkit import tomlkit.exceptions +import typer from comfy_cli import ui from comfy_cli.registry.types import ( ComfyConfig, + License, Model, ProjectConfig, PyProjectConfig, - License, URLs, ) @@ -70,21 +70,13 @@ def initialize_project_config(): # Get the current git remote URL try: - git_remote_url = ( - subprocess.check_output(["git", "remote", "get-url", "origin"]) - .decode() - .strip() - ) + git_remote_url = subprocess.check_output(["git", "remote", "get-url", "origin"]).decode().strip() except subprocess.CalledProcessError as e: - raise Exception( - "Could not retrieve Git remote URL. Are you in a Git repository?" - ) from e + raise Exception("Could not retrieve Git remote URL. Are you in a Git repository?") from e # Convert SSH URL to HTTPS if needed if git_remote_url.startswith("git@github.com:"): - git_remote_url = git_remote_url.replace( - "git@github.com:", "https://github.com/" - ) + git_remote_url = git_remote_url.replace("git@github.com:", "https://github.com/") # Ensure the URL ends with `.git` and remove it to obtain the plain URL repo_name = git_remote_url.rsplit("/", maxsplit=1)[-1].replace(".git", "") @@ -130,9 +122,7 @@ def extract_node_configuration( path: str = os.path.join(os.getcwd(), "pyproject.toml"), ) -> Optional[PyProjectConfig]: if not os.path.isfile(path): - ui.display_error_message( - "No pyproject.toml file found in the current directory." - ) + ui.display_error_message("No pyproject.toml file found in the current directory.") return None with open(path, "r") as file: @@ -150,9 +140,7 @@ def extract_node_configuration( ) elif isinstance(license_data, dict): if "file" in license_data or "text" in license_data: - license = License( - file=license_data.get("file", ""), text=license_data.get("text", "") - ) + license = License(file=license_data.get("file", ""), text=license_data.get("text", "")) else: typer.echo( 'Warning: License should be in one of these two formats: license = {file = "LICENSE"} OR license = {text = "MIT License"}. Please check the documentation: https://docs.comfy.org/registry/specifications.' @@ -183,10 +171,7 @@ def extract_node_configuration( publisher_id=comfy_data.get("PublisherId", ""), display_name=comfy_data.get("DisplayName", ""), icon=comfy_data.get("Icon", ""), - models=[ - Model(location=m["location"], model_url=m["model_url"]) - for m in comfy_data.get("Models", []) - ], + models=[Model(location=m["location"], model_url=m["model_url"]) for m in comfy_data.get("Models", [])], ) return PyProjectConfig(project=project, tool_comfy=comfy) diff --git a/comfy_cli/registry/types.py b/comfy_cli/registry/types.py index f33c06d..0c2b7eb 100644 --- a/comfy_cli/registry/types.py +++ b/comfy_cli/registry/types.py @@ -52,10 +52,13 @@ class ComfyConfig: icon: str = "" models: List[Model] = field(default_factory=list) + @dataclass class License: file: str = "" text: str = "" + + @dataclass class ProjectConfig: name: str = "" diff --git a/comfy_cli/tracking.py b/comfy_cli/tracking.py index 83b4da3..da0e993 100644 --- a/comfy_cli/tracking.py +++ b/comfy_cli/tracking.py @@ -44,9 +44,7 @@ def disable(): def track_event(event_name: str, properties: any = None): if properties is None: properties = {} - logging.debug( - f"tracking event called with event_name: {event_name} and properties: {properties}" - ) + logging.debug(f"tracking event called with event_name: {event_name} and properties: {properties}") enable_tracking = config_manager.get(constants.CONFIG_KEY_ENABLE_TRACKING) if not enable_tracking: return @@ -67,21 +65,13 @@ def track_command(sub_command: str = None): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): - command_name = ( - f"{sub_command}:{func.__name__}" - if sub_command is not None - else func.__name__ - ) + command_name = f"{sub_command}:{func.__name__}" if sub_command is not None else func.__name__ # Copy kwargs to avoid mutating original dictionary # Remove context and ctx from the dictionary as they are not needed for tracking and not serializable. - filtered_kwargs = { - k: v for k, v in kwargs.items() if k != "ctx" and k != "context" - } + filtered_kwargs = {k: v for k, v in kwargs.items() if k != "ctx" and k != "context"} - logging.debug( - f"Tracking command: {command_name} with arguments: {filtered_kwargs}" - ) + logging.debug(f"Tracking command: {command_name} with arguments: {filtered_kwargs}") track_event(command_name, properties=filtered_kwargs) return func(*args, **kwargs) @@ -99,9 +89,7 @@ def prompt_tracking_consent(skip_prompt: bool = False, default_value: bool = Fal if skip_prompt: init_tracking(default_value) else: - enable_tracking = ui.prompt_confirm_action( - "Do you agree to enable tracking to improve the application?", False - ) + enable_tracking = ui.prompt_confirm_action("Do you agree to enable tracking to improve the application?", False) init_tracking(enable_tracking) @@ -123,9 +111,7 @@ def init_tracking(enable_tracking: bool): # Note: only called once when the user interacts with the CLI for the # first time iff the permission is granted. - install_event_triggered = config_manager.get( - constants.CONFIG_KEY_INSTALL_EVENT_TRIGGERED - ) + install_event_triggered = config_manager.get(constants.CONFIG_KEY_INSTALL_EVENT_TRIGGERED) if not install_event_triggered: logging.debug("Tracking install event.") config_manager.set(constants.CONFIG_KEY_INSTALL_EVENT_TRIGGERED, "True") diff --git a/comfy_cli/ui.py b/comfy_cli/ui.py index 77054b0..b914d26 100644 --- a/comfy_cli/ui.py +++ b/comfy_cli/ui.py @@ -1,8 +1,9 @@ -from typing import List, Tuple, Optional, TypeVar, Union, Dict, Any from enum import Enum -from questionary import Choice +from typing import Any, Dict, List, Optional, Tuple, TypeVar, Union + import questionary import typer +from questionary import Choice from rich.console import Console from rich.progress import Progress from rich.table import Table @@ -36,11 +37,9 @@ 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 + 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. @@ -60,10 +59,7 @@ def prompt_autocomplete( def prompt_select( - question: str, - choices: List[ChoiceType], - default: ChoiceType = "", - force_prompting: bool = False + 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. @@ -82,13 +78,10 @@ def prompt_select( return questionary.select(question, choices=choices, default=default).ask() -E = TypeVar('E', bound=Enum) +E = TypeVar("E", bound=Enum) + -def prompt_select_enum( - question: str, - choices: List[E], - force_prompting: bool = False -) -> Optional[E]: +def prompt_select_enum(question: str, choices: List[E], force_prompting: bool = False) -> Optional[E]: """ Asks a single select question using questionary and returns the selected response. @@ -111,9 +104,7 @@ def prompt_select_enum( return choice_map[selected] if selected is not None else None -def prompt_input( - question: str, default: str = "", force_prompting: bool = False -) -> str: +def prompt_input(question: str, default: str = "", force_prompting: bool = False) -> str: """ Asks the user for an input using questionary. @@ -143,9 +134,7 @@ def prompt_multi_select(prompt: str, choices: List[str]) -> List[str]: Returns: List[str]: A list of the selected items. """ - selections = questionary.checkbox( - prompt, choices=choices - ).ask() # returns list of selected items + selections = questionary.checkbox(prompt, choices=choices).ask() # returns list of selected items return selections if selections else [] diff --git a/comfy_cli/update.py b/comfy_cli/update.py index c71efb3..698ecd6 100644 --- a/comfy_cli/update.py +++ b/comfy_cli/update.py @@ -27,9 +27,7 @@ def check_for_newer_pypi_version(package_name, current_version): def check_for_updates(): current_version = get_version_from_pyproject() - has_newer, newer_version = check_for_newer_pypi_version( - "comfy-cli", current_version - ) + has_newer, newer_version = check_for_newer_pypi_version("comfy-cli", current_version) if has_newer: notify_update(current_version, newer_version) diff --git a/comfy_cli/uv.py b/comfy_cli/uv.py index 4a45886..f90df56 100644 --- a/comfy_cli/uv.py +++ b/comfy_cli/uv.py @@ -71,10 +71,7 @@ class DependencyCompiler: @staticmethod def Find_Req_Files(*ders: PathLike) -> list[Path]: return [ - file - for der in ders - for file in Path(der).absolute().iterdir() - if file.name in DependencyCompiler.reqNames + file for der in ders for file in Path(der).absolute().iterdir() if file.name in DependencyCompiler.reqNames ] @staticmethod @@ -272,11 +269,7 @@ def __init__( outName: str = "requirements.compiled", ): self.cwd = Path(cwd) - self.reqFiles = ( - [Path(reqFile) for reqFile in reqFilesExt] - if reqFilesExt is not None - else None - ) + self.reqFiles = [Path(reqFile) for reqFile in reqFilesExt] if reqFilesExt is not None else None self.gpu = DependencyCompiler.Resolve_Gpu(gpu) self.gpuUrl = ( @@ -289,22 +282,14 @@ def __init__( self.out = self.cwd / outName self.override = self.cwd / "override.txt" - self.reqFilesCore = ( - reqFilesCore if reqFilesCore is not None else self.find_core_reqs() - ) - self.reqFilesExt = ( - reqFilesExt if reqFilesExt is not None else self.find_ext_reqs() - ) + self.reqFilesCore = reqFilesCore if reqFilesCore is not None else self.find_core_reqs() + self.reqFilesExt = reqFilesExt if reqFilesExt is not None else self.find_ext_reqs() def find_core_reqs(self): return DependencyCompiler.Find_Req_Files(self.cwd) def find_ext_reqs(self): - extDirs = [ - d - for d in (self.cwd / "custom_nodes").iterdir() - if d.is_dir() and d.name != "__pycache__" - ] + extDirs = [d for d in (self.cwd / "custom_nodes").iterdir() if d.is_dir() and d.name != "__pycache__"] return DependencyCompiler.Find_Req_Files(*extDirs) def make_override(self): @@ -313,21 +298,13 @@ def make_override(self): with open(self.override, "w") as f: if self.gpu is not None and self.gpuUrl is not None: - f.write( - DependencyCompiler.overrideGpu.format( - gpu=self.gpu, gpuUrl=self.gpuUrl - ) - ) + f.write(DependencyCompiler.overrideGpu.format(gpu=self.gpu, gpuUrl=self.gpuUrl)) f.write("\n\n") - completed = DependencyCompiler.Compile( - cwd=self.cwd, reqFiles=self.reqFilesCore, override=self.override - ) + completed = DependencyCompiler.Compile(cwd=self.cwd, reqFiles=self.reqFilesCore, override=self.override) with open(self.override, "a") as f: - f.write( - "# ensure that core comfyui deps take precedence over any 3rd party extension deps\n" - ) + f.write("# ensure that core comfyui deps take precedence over any 3rd party extension deps\n") for line in completed.stdout: f.write(line) f.write("\n") diff --git a/comfy_cli/workspace_manager.py b/comfy_cli/workspace_manager.py index 5c8b4c1..fc5f451 100644 --- a/comfy_cli/workspace_manager.py +++ b/comfy_cli/workspace_manager.py @@ -54,9 +54,7 @@ def check_comfy_repo(path) -> Tuple[bool, Optional[git.Repo]]: return False, None try: repo = git.Repo(path, search_parent_directories=True) - path_is_comfy_repo = any( - remote.url in constants.COMFY_ORIGIN_URL_CHOICES for remote in repo.remotes - ) + path_is_comfy_repo = any(remote.url in constants.COMFY_ORIGIN_URL_CHOICES for remote in repo.remotes) # If it's within the custom node repo, lookup from the parent directory. if not path_is_comfy_repo and "custom_nodes" in path: @@ -66,10 +64,7 @@ def check_comfy_repo(path) -> Tuple[bool, Optional[git.Repo]]: path = os.sep.join(parts[:index]) repo = git.Repo(path, search_parent_directories=True) - path_is_comfy_repo = any( - remote.url in constants.COMFY_ORIGIN_URL_CHOICES - for remote in repo.remotes - ) + path_is_comfy_repo = any(remote.url in constants.COMFY_ORIGIN_URL_CHOICES for remote in repo.remotes) except ValueError: pass @@ -175,25 +170,19 @@ def set_recent_workspace(self, path: str): """ Sets the most recent workspace path in the configuration. """ - self.config_manager.set( - constants.CONFIG_KEY_RECENT_WORKSPACE, os.path.abspath(path) - ) + self.config_manager.set(constants.CONFIG_KEY_RECENT_WORKSPACE, os.path.abspath(path)) def set_default_workspace(self, path: str): """ Sets the default workspace path in the configuration. """ - self.config_manager.set( - constants.CONFIG_KEY_DEFAULT_WORKSPACE, os.path.abspath(path) - ) + self.config_manager.set(constants.CONFIG_KEY_DEFAULT_WORKSPACE, os.path.abspath(path)) def set_default_launch_extras(self, extras: str): """ Sets the default workspace path in the configuration. """ - self.config_manager.set( - constants.CONFIG_KEY_DEFAULT_LAUNCH_EXTRAS, extras.strip() - ) + self.config_manager.set(constants.CONFIG_KEY_DEFAULT_LAUNCH_EXTRAS, extras.strip()) def __get_specified_workspace(self) -> Optional[str]: if self.specified_workspace is None: @@ -220,9 +209,7 @@ def get_workspace_path(self) -> Tuple[str, WorkspaceType]: # Check for recent workspace if requested if self.use_recent: - recent_workspace = self.config_manager.get( - constants.CONFIG_KEY_RECENT_WORKSPACE - ) + recent_workspace = self.config_manager.get(constants.CONFIG_KEY_RECENT_WORKSPACE) if recent_workspace: return recent_workspace, WorkspaceType.RECENT else: @@ -246,26 +233,20 @@ def get_workspace_path(self) -> Tuple[str, WorkspaceType]: # Check the current directory for a ComfyUI if self.use_here is None: current_directory = os.getcwd() - found_comfy_repo, comfy_repo = check_comfy_repo( - os.path.join(current_directory) - ) + found_comfy_repo, comfy_repo = check_comfy_repo(os.path.join(current_directory)) # If it's in a sub dir of the ComfyUI repo, get the repo working dir if found_comfy_repo: return comfy_repo.working_dir, WorkspaceType.CURRENT_DIR # Check for user-set default workspace - default_workspace = self.config_manager.get( - constants.CONFIG_KEY_DEFAULT_WORKSPACE - ) + default_workspace = self.config_manager.get(constants.CONFIG_KEY_DEFAULT_WORKSPACE) if default_workspace and check_comfy_repo(default_workspace)[0]: return default_workspace, WorkspaceType.DEFAULT # Fallback to the most recent workspace if it exists if self.use_recent is None: - recent_workspace = self.config_manager.get( - constants.CONFIG_KEY_RECENT_WORKSPACE - ) + recent_workspace = self.config_manager.get(constants.CONFIG_KEY_RECENT_WORKSPACE) if recent_workspace and check_comfy_repo(recent_workspace)[0]: return recent_workspace, WorkspaceType.RECENT else: @@ -282,9 +263,7 @@ def get_comfyui_manager_path(self): return None # To check more robustly, verify up to the `.git` path. - manager_path = os.path.join( - self.workspace_path, "custom_nodes", "ComfyUI-Manager" - ) + manager_path = os.path.join(self.workspace_path, "custom_nodes", "ComfyUI-Manager") return manager_path def is_comfyui_manager_installed(self): @@ -292,9 +271,7 @@ def is_comfyui_manager_installed(self): return False # To check more robustly, verify up to the `.git` path. - manager_git_path = os.path.join( - self.workspace_path, "custom_nodes", "ComfyUI-Manager", ".git" - ) + manager_git_path = os.path.join(self.workspace_path, "custom_nodes", "ComfyUI-Manager", ".git") return os.path.exists(manager_git_path) def scan_dir(self): @@ -315,9 +292,7 @@ def scan_dir_concur(self): # Use ThreadPoolExecutor to manage concurrency with concurrent.futures.ThreadPoolExecutor() as executor: - futures = [ - executor.submit(check_file_is_model, p) for p in base_path.rglob("*") - ] + futures = [executor.submit(check_file_is_model, p) for p in base_path.rglob("*")] for future in concurrent.futures.as_completed(futures): if future.result(): model_files.append(future.result()) diff --git a/tests/comfy_cli/command/test_command.py b/tests/comfy_cli/command/test_command.py index 2044f5b..7dcbac9 100644 --- a/tests/comfy_cli/command/test_command.py +++ b/tests/comfy_cli/command/test_command.py @@ -22,9 +22,7 @@ def mock_execute(): @pytest.fixture(scope="function") def mock_prompt_select_enum(): - def mocked_prompt_select_enum( - question: str, choices: list, force_prompting: bool = False - ): + def mocked_prompt_select_enum(question: str, choices: list, force_prompting: bool = False): return choices[0] with patch( diff --git a/tests/comfy_cli/registry/test_api.py b/tests/comfy_cli/registry/test_api.py index 5cede7c..deab201 100644 --- a/tests/comfy_cli/registry/test_api.py +++ b/tests/comfy_cli/registry/test_api.py @@ -3,7 +3,7 @@ from comfy_cli.registry import PyProjectConfig from comfy_cli.registry.api import RegistryAPI -from comfy_cli.registry.types import ComfyConfig, ProjectConfig, URLs, License +from comfy_cli.registry.types import ComfyConfig, License, ProjectConfig, URLs class TestRegistryAPI(unittest.TestCase): @@ -30,16 +30,12 @@ def setUp(self): @patch("os.getenv") def test_determine_base_url_dev(self, mock_getenv): mock_getenv.return_value = "dev" - self.assertEqual( - self.registry_api.determine_base_url(), "http://localhost:8080" - ) + self.assertEqual(self.registry_api.determine_base_url(), "http://localhost:8080") @patch("os.getenv") def test_determine_base_url_prod(self, mock_getenv): mock_getenv.return_value = "prod" - self.assertEqual( - self.registry_api.determine_base_url(), "https://api.comfy.org" - ) + self.assertEqual(self.registry_api.determine_base_url(), "https://api.comfy.org") @patch("requests.post") def test_publish_node_version_success(self, mock_post): diff --git a/tests/comfy_cli/registry/test_config_parser.py b/tests/comfy_cli/registry/test_config_parser.py index 4b2fb30..fdebeae 100644 --- a/tests/comfy_cli/registry/test_config_parser.py +++ b/tests/comfy_cli/registry/test_config_parser.py @@ -1,13 +1,13 @@ -from unittest.mock import patch, mock_open +from unittest.mock import mock_open, patch + import pytest + from comfy_cli.registry.config_parser import extract_node_configuration from comfy_cli.registry.types import ( - PyProjectConfig, - ProjectConfig, License, - URLs, - ComfyConfig, Model, + PyProjectConfig, + URLs, ) @@ -49,9 +49,11 @@ def mock_toml_data(): def test_extract_node_configuration_success(mock_toml_data): - with patch("os.path.isfile", return_value=True), patch( - "builtins.open", mock_open() - ), patch("tomlkit.load", return_value=mock_toml_data): + with ( + patch("os.path.isfile", return_value=True), + patch("builtins.open", mock_open()), + patch("tomlkit.load", return_value=mock_toml_data), + ): result = extract_node_configuration("fake_path.toml") assert isinstance(result, PyProjectConfig) @@ -71,9 +73,7 @@ def test_extract_node_configuration_success(mock_toml_data): assert result.tool_comfy.display_name == "Test Project" assert result.tool_comfy.icon == "icon.png" assert len(result.tool_comfy.models) == 2 - assert result.tool_comfy.models[0] == Model( - location="model1.bin", model_url="https://example.com/model1" - ) + assert result.tool_comfy.models[0] == Model(location="model1.bin", model_url="https://example.com/model1") def test_extract_node_configuration_license_text(): @@ -82,9 +82,11 @@ def test_extract_node_configuration_license_text(): "license": "MIT License", }, } - with patch("os.path.isfile", return_value=True), patch( - "builtins.open", mock_open() - ), patch("tomlkit.load", return_value=mock_data): + with ( + patch("os.path.isfile", return_value=True), + patch("builtins.open", mock_open()), + patch("tomlkit.load", return_value=mock_data), + ): result = extract_node_configuration("fake_path.toml") assert result is not None, "Expected PyProjectConfig, got None" assert isinstance(result, PyProjectConfig) @@ -94,14 +96,14 @@ def test_extract_node_configuration_license_text(): def test_extract_node_configuration_license_text_dict(): mock_data = { "project": { - "license": { - "text": "MIT License\n\nCopyright (c) 2023 Example Corp\n\nPermission is hereby granted..." - }, + "license": {"text": "MIT License\n\nCopyright (c) 2023 Example Corp\n\nPermission is hereby granted..."}, }, } - with patch("os.path.isfile", return_value=True), patch( - "builtins.open", mock_open() - ), patch("tomlkit.load", return_value=mock_data): + with ( + patch("os.path.isfile", return_value=True), + patch("builtins.open", mock_open()), + patch("tomlkit.load", return_value=mock_data), + ): result = extract_node_configuration("fake_path.toml") assert result is not None, "Expected PyProjectConfig, got None" @@ -115,9 +117,11 @@ def test_extract_license_incorrect_format(): mock_data = { "project": {"license": "MIT"}, } - with patch("os.path.isfile", return_value=True), patch( - "builtins.open", mock_open() - ), patch("tomlkit.load", return_value=mock_data): + with ( + patch("os.path.isfile", return_value=True), + patch("builtins.open", mock_open()), + patch("tomlkit.load", return_value=mock_data), + ): result = extract_node_configuration("fake_path.toml") assert result is not None, "Expected PyProjectConfig, got None" diff --git a/tests/e2e/test_e2e.py b/tests/e2e/test_e2e.py index eae4c3a..5c0dce1 100644 --- a/tests/e2e/test_e2e.py +++ b/tests/e2e/test_e2e.py @@ -162,9 +162,7 @@ def test_run(comfy_cli): ) assert 0 == proc.returncode - workflow = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "workflow.json" - ) + workflow = os.path.join(os.path.dirname(os.path.realpath(__file__)), "workflow.json") proc = exec( f""" {comfy_cli} run --workflow {workflow} --wait --timeout 180 diff --git a/tests/uv/test_uv.py b/tests/uv/test_uv.py index e7d5e07..d55c6d6 100644 --- a/tests/uv/test_uv.py +++ b/tests/uv/test_uv.py @@ -1,42 +1,46 @@ +import shutil from pathlib import Path + import pytest -import shutil -from comfy_cli.uv import DependencyCompiler from comfy_cli import ui +from comfy_cli.uv import DependencyCompiler hereDir = Path(__file__).parent.resolve() -reqsDir = hereDir/"mock_requirements" +reqsDir = hereDir / "mock_requirements" # set up a temp dir to write files to testsDir = hereDir.parent.resolve() -temp = testsDir/"temp"/"test_uv" +temp = testsDir / "temp" / "test_uv" shutil.rmtree(temp, ignore_errors=True) temp.mkdir(exist_ok=True, parents=True) + @pytest.fixture def mock_prompt_select(monkeypatch): mockChoices = ["==1.13.0", "==2.0.0"] + def _mock_prompt_select(*args, **kwargs): return mockChoices.pop(0) monkeypatch.setattr(ui, "prompt_select", _mock_prompt_select) + def test_compile(mock_prompt_select): depComp = DependencyCompiler( cwd=temp, - reqFilesCore=[reqsDir/"core_reqs.txt"], - reqFilesExt=[reqsDir/"x_reqs.txt", reqsDir/"y_reqs.txt"], + reqFilesCore=[reqsDir / "core_reqs.txt"], + reqFilesExt=[reqsDir / "x_reqs.txt", reqsDir / "y_reqs.txt"], ) DependencyCompiler.Install_Build_Deps() depComp.make_override() depComp.compile_core_plus_ext() - with open(reqsDir/"requirements.compiled", "r") as known, open(temp/"requirements.compiled", "r") as test: + with open(reqsDir / "requirements.compiled", "r") as known, open(temp / "requirements.compiled", "r") as test: # compare all non-commented lines in generated file vs reference file knownLines, testLines = [ - [line for line in known.readlines() if line.strip()[0]!="#"], - [line for line in test.readlines() if line.strip()[0]!="#"], + [line for line in known.readlines() if line.strip()[0] != "#"], + [line for line in test.readlines() if line.strip()[0] != "#"], ] assert knownLines == testLines From 49ee6907083e62e38b03e160bb3f7a395f8d4348 Mon Sep 17 00:00:00 2001 From: telamonian Date: Fri, 16 Aug 2024 22:18:31 -0400 Subject: [PATCH 10/10] fixed all initial non-autofixable complaints of new linter `ruff check` --- comfy_cli/command/__init__.py | 2 ++ comfy_cli/command/custom_nodes/__init__.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/comfy_cli/command/__init__.py b/comfy_cli/command/__init__.py index 20a8c22..ca9548d 100644 --- a/comfy_cli/command/__init__.py +++ b/comfy_cli/command/__init__.py @@ -1 +1,3 @@ from . import custom_nodes, install + +__all__ = ["custom_nodes", "install"] diff --git a/comfy_cli/command/custom_nodes/__init__.py b/comfy_cli/command/custom_nodes/__init__.py index fb2172e..18d4ec8 100644 --- a/comfy_cli/command/custom_nodes/__init__.py +++ b/comfy_cli/command/custom_nodes/__init__.py @@ -1 +1,3 @@ from .command import app, manager_app + +__all__ = ["app", "manager_app"]