-
Notifications
You must be signed in to change notification settings - Fork 34
/
nodes_registry.py
128 lines (97 loc) · 4.48 KB
/
nodes_registry.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import re
from typing import Callable, Optional, Type
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
NODES_DISPLAY_NAME_PREFIX = "🅛🅣🅧"
EXPERIMENTAL_DISPLAY_NAME_PREFIX = "(Experimental 🧪)"
DEPRECATED_DISPLAY_NAME_PREFIX = "(Deprecated 🚫)"
DEFAULT_CATEGORY_NAME = "Lightricks"
def register_node(node_class: Type, name: str, description: str) -> None:
"""
Register a ComfyUI node class to ComfyUI's global nodes' registry.
Args:
node_class (Type): The class of the node to be registered.
name (str): The name of the node.
description (str): The short user-friendly description of the node.
Raises:
ValueError: If `node_class` is not a class, or `class_name` or `display_name` is not a string.
"""
if not isinstance(node_class, type):
raise ValueError("`node_class` must be a class")
if not isinstance(name, str):
raise ValueError("`name` must be a string")
if not isinstance(description, str):
raise ValueError("`description` must be a string")
NODE_CLASS_MAPPINGS[name] = node_class
NODE_DISPLAY_NAME_MAPPINGS[name] = description
def comfy_node(
node_class: Optional[Type] = None,
name: Optional[str] = None,
description: Optional[str] = None,
experimental: bool = False,
deprecated: bool = False,
skip: bool = False,
) -> Callable:
"""
Decorator for registering a node class with optional name, description, and status flags.
Args:
node_class (Type): The class of the node to be registered.
name (str, optional): The name of the class. If not provided, the class name will be used.
description (str, optional): The description of the class.
If not provided, an auto-formatted description will be used based on the class name.
experimental (bool): Flag indicating if the class is experimental. Defaults to False.
deprecated (bool): Flag indicating if the class is deprecated. Defaults to False.
skip (bool): Flag indicating if the node registration should be skipped. Defaults to False.
This is useful for conditionally registering nodes based on certain conditions
(e.g. unavailability of certain dependencies).
Returns:
Callable: The decorator function.
Raises:
ValueError: If `node_class` is not a class.
"""
def decorator(node_class: Type) -> Type:
if skip:
return node_class
if not isinstance(node_class, type):
raise ValueError("`node_class` must be a class")
nonlocal name, description
if name is None:
name = node_class.__name__
# Remove possible "Node" suffix from the class name, e.g. "EditImageNode -> EditImage"
if name is not None and name.endswith("Node"):
name = name[:-4]
description = _format_description(description, name, experimental, deprecated)
register_node(node_class, name, description)
return node_class
# If the decorator is used without parentheses
if node_class is None:
return decorator
else:
return decorator(node_class)
def _format_description(
description: str, class_name: str, experimental: bool, deprecated: bool
) -> str:
"""Format nodes display name to a standard format"""
# If description is not provided, auto-generate one based on the class name
if description is None:
description = _camel_case_to_spaces(class_name)
# Strip the prefix if it's already there
prefix_len = len(NODES_DISPLAY_NAME_PREFIX)
if description.startswith(NODES_DISPLAY_NAME_PREFIX):
description = description[prefix_len:].lstrip()
# Add the deprecated / experimental prefixes
if deprecated:
description = f"{DEPRECATED_DISPLAY_NAME_PREFIX} {description}"
elif experimental:
description = f"{EXPERIMENTAL_DISPLAY_NAME_PREFIX} {description}"
# Add the prefix
description = f"{NODES_DISPLAY_NAME_PREFIX} {description}"
return description
def _camel_case_to_spaces(text: str) -> str:
# Add space before each capital letter except the first one
spaced_text = re.sub(r"(?<=[a-z])(?=[A-Z])", " ", text)
# Handle sequences of uppercase letters followed by a lowercase letter
spaced_text = re.sub(r"(?<=[A-Z])(?=[A-Z][a-z])", " ", spaced_text)
# Handle sequences of uppercase letters not followed by a lowercase letter
spaced_text = re.sub(r"(?<=[A-Z])(?=[A-Z][A-Z][a-z])", " ", spaced_text)
return spaced_text