mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-15 11:37:32 +08:00
Attach pyproject.toml node-pack identity to startup error entries
When a failing module has a pyproject.toml, parse it via comfy_config.config_parser and attach a 'pyproject' field with the Comfy Registry-style identity (pack_id, display_name, publisher_id, version, repository). This gives the frontend/Manager a stable, user-recognizable handle for the failed pack beyond the on-disk folder name. The lookup is best-effort and never raises: missing toml, missing pydantic-settings dependency, or any parse error simply omits the 'pyproject' key. Ref: ComfyUI-Launcher#303 Amp-Thread-ID: https://ampcode.com/threads/T-019e23a1-2acc-7619-bd0e-f783d1368ef3 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
parent
3a649984f2
commit
af55a2308f
39
nodes.py
39
nodes.py
@ -2202,12 +2202,45 @@ def _node_source_from_parent(module_parent: str) -> str:
|
|||||||
NODE_STARTUP_ERRORS: dict[str, dict] = {}
|
NODE_STARTUP_ERRORS: dict[str, dict] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def _read_pyproject_metadata(module_path: str) -> dict | None:
|
||||||
|
"""Best-effort extraction of node-pack identity from pyproject.toml.
|
||||||
|
|
||||||
|
Returns a dict with the Comfy Registry-style identity (pack_id,
|
||||||
|
display_name, publisher_id, version, repository) when the module
|
||||||
|
directory contains a pyproject.toml. Returns None when no toml is
|
||||||
|
present or parsing fails for any reason — startup-error tracking
|
||||||
|
must never itself raise.
|
||||||
|
"""
|
||||||
|
if not module_path or not os.path.isdir(module_path):
|
||||||
|
return None
|
||||||
|
toml_path = os.path.join(module_path, "pyproject.toml")
|
||||||
|
if not os.path.isfile(toml_path):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
from comfy_config import config_parser
|
||||||
|
|
||||||
|
cfg = config_parser.extract_node_configuration(module_path)
|
||||||
|
if cfg is None:
|
||||||
|
return None
|
||||||
|
meta = {
|
||||||
|
"pack_id": cfg.project.name or None,
|
||||||
|
"display_name": cfg.tool_comfy.display_name or None,
|
||||||
|
"publisher_id": cfg.tool_comfy.publisher_id or None,
|
||||||
|
"version": cfg.project.version or None,
|
||||||
|
"repository": cfg.project.urls.repository or None,
|
||||||
|
}
|
||||||
|
# Drop empty fields so the API payload stays compact.
|
||||||
|
return {k: v for k, v in meta.items() if v}
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def record_node_startup_error(
|
def record_node_startup_error(
|
||||||
*, module_path: str, source: str, phase: str, error: BaseException, tb: str
|
*, module_path: str, source: str, phase: str, error: BaseException, tb: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Record a startup error for a node module so it can be exposed via the API."""
|
"""Record a startup error for a node module so it can be exposed via the API."""
|
||||||
module_name = get_module_name(module_path)
|
module_name = get_module_name(module_path)
|
||||||
NODE_STARTUP_ERRORS[f"{source}:{module_name}"] = {
|
entry = {
|
||||||
"source": source,
|
"source": source,
|
||||||
"module_name": module_name,
|
"module_name": module_name,
|
||||||
"module_path": module_path,
|
"module_path": module_path,
|
||||||
@ -2215,6 +2248,10 @@ def record_node_startup_error(
|
|||||||
"traceback": tb,
|
"traceback": tb,
|
||||||
"phase": phase,
|
"phase": phase,
|
||||||
}
|
}
|
||||||
|
pyproject = _read_pyproject_metadata(module_path)
|
||||||
|
if pyproject:
|
||||||
|
entry["pyproject"] = pyproject
|
||||||
|
NODE_STARTUP_ERRORS[f"{source}:{module_name}"] = entry
|
||||||
|
|
||||||
|
|
||||||
def get_module_name(module_path: str) -> str:
|
def get_module_name(module_path: str) -> str:
|
||||||
|
|||||||
@ -97,6 +97,47 @@ async def test_load_custom_node_collision_across_sources(tmp_path):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_load_custom_node_attaches_pyproject_metadata(tmp_path):
|
||||||
|
pack_dir = tmp_path / "MyCoolPack"
|
||||||
|
pack_dir.mkdir()
|
||||||
|
(pack_dir / "__init__.py").write_text("raise RuntimeError('boom')\n")
|
||||||
|
(pack_dir / "pyproject.toml").write_text(textwrap.dedent("""\
|
||||||
|
[project]
|
||||||
|
name = "comfyui-mycoolpack"
|
||||||
|
version = "1.2.3"
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Repository = "https://github.com/example/comfyui-mycoolpack"
|
||||||
|
|
||||||
|
[tool.comfy]
|
||||||
|
PublisherId = "example"
|
||||||
|
DisplayName = "My Cool Pack"
|
||||||
|
"""))
|
||||||
|
|
||||||
|
success = await nodes.load_custom_node(str(pack_dir), module_parent="custom_nodes")
|
||||||
|
assert success is False
|
||||||
|
|
||||||
|
entry = nodes.NODE_STARTUP_ERRORS["custom_node:MyCoolPack"]
|
||||||
|
assert "pyproject" in entry, entry
|
||||||
|
py = entry["pyproject"]
|
||||||
|
assert py["pack_id"] == "comfyui-mycoolpack"
|
||||||
|
assert py["display_name"] == "My Cool Pack"
|
||||||
|
assert py["publisher_id"] == "example"
|
||||||
|
assert py["version"] == "1.2.3"
|
||||||
|
assert py["repository"] == "https://github.com/example/comfyui-mycoolpack"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_load_custom_node_no_pyproject_skips_metadata(tmp_path):
|
||||||
|
# Single-file extras-style module: no pyproject.toml exists alongside it,
|
||||||
|
# so the entry must not contain a 'pyproject' key.
|
||||||
|
module_path = _write_broken_module(tmp_path, "lonely")
|
||||||
|
assert await nodes.load_custom_node(module_path, module_parent="comfy_extras") is False
|
||||||
|
entry = nodes.NODE_STARTUP_ERRORS["comfy_extra:lonely"]
|
||||||
|
assert "pyproject" not in entry
|
||||||
|
|
||||||
|
|
||||||
def test_unknown_module_parent_defaults_to_custom_node():
|
def test_unknown_module_parent_defaults_to_custom_node():
|
||||||
assert nodes._node_source_from_parent("custom_nodes") == "custom_node"
|
assert nodes._node_source_from_parent("custom_nodes") == "custom_node"
|
||||||
assert nodes._node_source_from_parent("comfy_extras") == "comfy_extra"
|
assert nodes._node_source_from_parent("comfy_extras") == "comfy_extra"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user