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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 53 additions & 32 deletions comfy_cli/registry/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import tomlkit
import tomlkit.exceptions
import tomlkit.items
import typer

from comfy_cli import ui
Expand Down Expand Up @@ -44,6 +45,49 @@
)


# Uncommentable hint emitted below "[tool.comfy].includes".
_INCLUDES_HINT = """
# "requires-comfyui" = ">=1.0.0" # ComfyUI version compatibility
"""

# Uncommentable hint emitted below "[project].license", for OS/GPU support.
_CLASSIFIERS_HINT = """
# classifiers = [
# # For OS-independent nodes (works on all operating systems)
# "Operating System :: OS Independent",
#
# # OR for OS-specific nodes, specify the supported systems:
# "Operating System :: Microsoft :: Windows", # Windows specific
# "Operating System :: POSIX :: Linux", # Linux specific
# "Operating System :: MacOS", # macOS specific
#
# # GPU Accelerator support. Pick the ones that are supported by your extension.
# "Environment :: GPU :: NVIDIA CUDA", # NVIDIA CUDA support
# "Environment :: GPU :: AMD ROCm", # AMD ROCm support
# "Environment :: GPU :: Intel Arc", # Intel Arc support
# "Environment :: NPU :: Huawei Ascend", # Huawei Ascend support
# "Environment :: GPU :: Apple Metal", # Apple Metal support
# ]
"""


def _add_commented_hint(table: tomlkit.items.Table, hint: str) -> None:
"""Append `hint` to `table` as standalone comment lines.

tomlkit's `Item.comment()` takes a single line by contract — it raises
`ValueError` on line breaks as of tomlkit 0.15.1 — and renders inline, which
would leave the hint impossible to uncomment in place. So emit one standalone
comment per line instead. `tomlkit.comment()` re-adds the `#` marker, hence
the strip. Hints must be added while `table` is still being built, before any
sub-table: a comment appended after one renders *inside* that sub-table.
"""
# `strip()` (not `strip("\n")`) so a CRLF checkout of this file — the repo has
# no `.gitattributes` and `core.autocrlf=true` is the Windows default — doesn't
# leave a leading `\r` that `splitlines()` would turn into a stray empty `#`.
for line in hint.strip().splitlines():
table.add(tomlkit.comment(line.removeprefix("#").removeprefix(" ")))


def create_comfynode_config():
# Create the initial structure of the TOML document
document = tomlkit.document()
Expand All @@ -55,6 +99,11 @@ def create_comfynode_config():
project["dependencies"] = tomlkit.aot()
project["license"] = "MIT"

# Attach the classifiers hint below "license", before the "urls" sub-table is
# added — `initialize_project_config` reparses this file and only rewrites
# values, so this is the one place the hint can be positioned correctly.
_add_commented_hint(project, _CLASSIFIERS_HINT)

urls = tomlkit.table()
urls["Repository"] = ""

Expand All @@ -72,9 +121,7 @@ def create_comfynode_config():
comfy["includes"] = tomlkit.array()

# Add uncommentable hint for ComfyUI version compatibility, below of "[tool.comfy].includes" field.
comfy["includes"].comment("""
# "requires-comfyui" = ">=1.0.0" # ComfyUI version compatibility
""")
_add_commented_hint(comfy, _INCLUDES_HINT)

tool.add("comfy", comfy)
document.add("tool", tool)
Expand Down Expand Up @@ -236,35 +283,9 @@ def initialize_project_config():
# Use PEP 639 SPDX license identifier
project["license"] = "MIT"

# [project].classifiers Classifiers uncommentable hint for OS/GPU support
# Attach classifiers comments to the project, below of "license" field.
# will generate a comment like this:
#
# [project]
# ...
# license = "MIT"
# # classifiers = [
# # # For OS-independent nodes (works on all operating systems)
# ...

project["license"].comment("""
# classifiers = [
# # For OS-independent nodes (works on all operating systems)
# "Operating System :: OS Independent",
#
# # OR for OS-specific nodes, specify the supported systems:
# "Operating System :: Microsoft :: Windows", # Windows specific
# "Operating System :: POSIX :: Linux", # Linux specific
# "Operating System :: MacOS", # macOS specific
#
# # GPU Accelerator support. Pick the ones that are supported by your extension.
# "Environment :: GPU :: NVIDIA CUDA", # NVIDIA CUDA support
# "Environment :: GPU :: AMD ROCm", # AMD ROCm support
# "Environment :: GPU :: Intel Arc", # Intel Arc support
# "Environment :: NPU :: Huawei Ascend", # Huawei Ascend support
# "Environment :: GPU :: Apple Metal", # Apple Metal support
# ]
""")
# The [project].classifiers hint is emitted by `create_comfynode_config` above,
# which is the only place it can be positioned below "license" (see
# `_add_commented_hint`). Reassigning "license" here preserves it.

tool = document.get("tool", tomlkit.table())
comfy = tool.get("comfy", tomlkit.table())
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ dependencies = [
"rich",
"ruff",
"semver~=3.0.2",
"tomlkit",
"tomlkit>=0.13,<0.16",
"typer>=0.12.5",
"typing-extensions>=4.7",
"uv>=0.11.15",
Expand Down
63 changes: 63 additions & 0 deletions tests/comfy_cli/registry/test_config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import tomlkit

from comfy_cli.registry.config_parser import (
_add_commented_hint,
_strip_url_credentials,
create_comfynode_config,
extract_node_configuration,
initialize_project_config,
validate_and_extract_accelerator_classifiers,
Expand Down Expand Up @@ -1066,3 +1068,64 @@ def test_initialize_project_config_vcs_with_inline_comment(tmp_path, monkeypatch
data = tomlkit.parse(f.read())
deps = [str(d) for d in data["project"]["dependencies"]]
assert deps == ["git+https://github.com/org/mono.git#subdirectory=pkg"]


# The `[tool.comfy]` version-compatibility hint is emitted as a standalone comment.
# tomlkit's `Item.comment()` is single-line by contract (it raises on line breaks as
# of 0.15.1) and renders inline, which would leave the hint un-uncommentable.


def test_create_comfynode_config_emits_uncommentable_version_hint(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
create_comfynode_config()
rendered = (tmp_path / "pyproject.toml").read_text()

# The hint sits on its own line beneath `includes`, not trailing it inline.
assert '\nincludes = []\n# "requires-comfyui" = ">=1.0.0" # ComfyUI version compatibility\n' in rendered

# It is inert until uncommented...
assert "requires-comfyui" not in tomlkit.parse(rendered)["tool"]["comfy"]

# ...and uncommenting it in place yields a valid key.
uncommented = rendered.replace('# "requires-comfyui"', '"requires-comfyui"', 1)
assert tomlkit.parse(uncommented)["tool"]["comfy"]["requires-comfyui"] == ">=1.0.0"


def test_initialize_project_config_emits_uncommentable_classifiers_hint(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
_init_git_repo_with_reqs(tmp_path, "")
initialize_project_config()
rendered = (tmp_path / "pyproject.toml").read_text()

# The hint sits under `license`, and critically *before* the `[project.urls]`
# sub-table — appending it afterwards would scope `classifiers` to the wrong table.
assert '\nlicense = "MIT"\n# classifiers = [\n' in rendered
assert rendered.index("# classifiers = [") < rendered.index("[project.urls]")
assert '# "Operating System :: OS Independent",\n' in rendered
assert "\n# ]\n" in rendered

# It is inert until uncommented...
assert "classifiers" not in tomlkit.parse(rendered)["project"]

# ...and uncommenting the block in place yields a valid `[project].classifiers`.
hint_start = rendered.index("# classifiers = [")
hint_end = rendered.index("# ]", hint_start) + len("# ]")
block = rendered[hint_start:hint_end]
uncommented = "\n".join(line[1:].lstrip(" ") if line.strip() != "#" else "" for line in block.splitlines())
parsed = tomlkit.parse(rendered[:hint_start] + uncommented + rendered[hint_end:])
assert "Operating System :: OS Independent" in parsed["project"]["classifiers"]
assert "Environment :: GPU :: Apple Metal" in parsed["project"]["classifiers"]


def test_add_commented_hint_tolerates_crlf_source():
# If this source file is checked out with CRLF endings (no `.gitattributes` +
# Windows `core.autocrlf=true`), the hint literals contain `\r\n`. A leading
# `\r` surviving into `splitlines()` would emit a stray empty `#` comment above
# the hint. Simulate that by feeding a CRLF hint and asserting no bare `#` line.
table = tomlkit.table()
_add_commented_hint(table, '\r\n# "requires-comfyui" = ">=1.0.0" # note\r\n')
rendered = tomlkit.document()
rendered["tool"] = table
lines = tomlkit.dumps(rendered).splitlines()
assert "#" not in [line.strip() for line in lines]
assert '# "requires-comfyui" = ">=1.0.0" # note' in rendered.as_string()
Loading