Consolidate boolean, casting, control flow, data list and dict nodes

This commit is contained in:
StableLlama
2025-05-11 16:18:45 +02:00
parent 22bb1569ba
commit 2e4dadaa80
5 changed files with 235 additions and 490 deletions
+24 -23
View File
@@ -1,6 +1,7 @@
from inspect import cleandoc
from comfy.comfy_types.node_typing import IO, ComfyNodeABC
class BooleanAnd:
class BooleanAnd(ComfyNodeABC):
"""
Returns the logical AND result of two boolean values.
@@ -10,12 +11,12 @@ class BooleanAnd:
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False, "forceInput": True}),
"input2": ("BOOLEAN", {"default": False, "forceInput": True}),
"input1": (IO.BOOLEAN, {"default": False, "forceInput": True}),
"input2": (IO.BOOLEAN, {"default": False, "forceInput": True}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_TYPES = (IO.BOOLEAN,)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "and_operation"
@@ -24,7 +25,7 @@ class BooleanAnd:
return (input1 and input2,)
class BooleanOr:
class BooleanOr(ComfyNodeABC):
"""
Returns the logical OR result of two boolean values.
@@ -34,12 +35,12 @@ class BooleanOr:
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False, "forceInput": True}),
"input2": ("BOOLEAN", {"default": False, "forceInput": True}),
"input1": (IO.BOOLEAN, {"default": False, "forceInput": True}),
"input2": (IO.BOOLEAN, {"default": False, "forceInput": True}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_TYPES = (IO.BOOLEAN,)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "or_operation"
@@ -48,7 +49,7 @@ class BooleanOr:
return (input1 or input2,)
class BooleanNot:
class BooleanNot(ComfyNodeABC):
"""
Returns the logical NOT result of a boolean value.
@@ -58,11 +59,11 @@ class BooleanNot:
def INPUT_TYPES(cls):
return {
"required": {
"input": ("BOOLEAN", {"default": False, "forceInput": True}),
"input": (IO.BOOLEAN, {"default": False, "forceInput": True}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_TYPES = (IO.BOOLEAN,)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "not_operation"
@@ -71,7 +72,7 @@ class BooleanNot:
return (not input,)
class BooleanXor:
class BooleanXor(ComfyNodeABC):
"""
Returns the logical XOR result of two boolean values.
@@ -81,12 +82,12 @@ class BooleanXor:
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False, "forceInput": True}),
"input2": ("BOOLEAN", {"default": False, "forceInput": True}),
"input1": (IO.BOOLEAN, {"default": False, "forceInput": True}),
"input2": (IO.BOOLEAN, {"default": False, "forceInput": True}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_TYPES = (IO.BOOLEAN,)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "xor_operation"
@@ -95,7 +96,7 @@ class BooleanXor:
return (input1 != input2,)
class BooleanNand:
class BooleanNand(ComfyNodeABC):
"""
Returns the logical NAND result of two boolean values.
@@ -105,12 +106,12 @@ class BooleanNand:
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False, "forceInput": True}),
"input2": ("BOOLEAN", {"default": False, "forceInput": True}),
"input1": (IO.BOOLEAN, {"default": False, "forceInput": True}),
"input2": (IO.BOOLEAN, {"default": False, "forceInput": True}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_TYPES = (IO.BOOLEAN,)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "nand_operation"
@@ -119,7 +120,7 @@ class BooleanNand:
return (not (input1 and input2),)
class BooleanNor:
class BooleanNor(ComfyNodeABC):
"""
Returns the logical NOR result of two boolean values.
@@ -129,12 +130,12 @@ class BooleanNor:
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False, "forceInput": True}),
"input2": ("BOOLEAN", {"default": False, "forceInput": True}),
"input1": (IO.BOOLEAN, {"default": False, "forceInput": True}),
"input2": (IO.BOOLEAN, {"default": False, "forceInput": True}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_TYPES = (IO.BOOLEAN,)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "nor_operation"
+21 -57
View File
@@ -1,8 +1,8 @@
from typing import Any
from inspect import cleandoc
from comfy.comfy_types.node_typing import IO, ComfyNodeABC
class CastToString:
class CastToString(ComfyNodeABC):
"""
Converts any input to a STRING. Non-string values are converted using str().
"""
@@ -10,24 +10,20 @@ class CastToString:
def INPUT_TYPES(cls):
return {
"required": {
"input": ("*", {})
"input": (IO.ANY, {})
}
}
RETURN_TYPES = ("STRING",)
RETURN_TYPES = (IO.STRING,)
CATEGORY = "Basic/cast"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "convert_to_string"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert_to_string(self, input: Any) -> tuple[str]:
return (str(input),)
class CastToInt:
class CastToInt(ComfyNodeABC):
"""
Converts any numeric input to an INT. Non-numeric or invalid inputs raise a ValueError.
"""
@@ -35,19 +31,15 @@ class CastToInt:
def INPUT_TYPES(cls):
return {
"required": {
"input": ("*", {})
"input": (IO.ANY, {})
}
}
RETURN_TYPES = ("INT",)
RETURN_TYPES = (IO.INT,)
CATEGORY = "Basic/cast"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "convert_to_int"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert_to_int(self, input: Any) -> tuple[int]:
try:
return (int(input),)
@@ -55,7 +47,7 @@ class CastToInt:
raise ValueError(f"Cannot convert {input} to an INT.")
class CastToFloat:
class CastToFloat(ComfyNodeABC):
"""
Converts any numeric input to a FLOAT. Non-numeric or invalid inputs raise a ValueError.
"""
@@ -63,7 +55,7 @@ class CastToFloat:
def INPUT_TYPES(cls):
return {
"required": {
"input": ("*", {})
"input": (IO.ANY, {})
}
}
@@ -72,10 +64,6 @@ class CastToFloat:
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "convert_to_float"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert_to_float(self, input: Any) -> tuple[float]:
try:
return (float(input),)
@@ -83,7 +71,7 @@ class CastToFloat:
raise ValueError(f"Cannot convert {input} to a FLOAT.")
class CastToBoolean:
class CastToBoolean(ComfyNodeABC):
"""
Converts any input to a BOOLEAN. Follows standard Python truthy/falsy rules.
"""
@@ -91,7 +79,7 @@ class CastToBoolean:
def INPUT_TYPES(cls):
return {
"required": {
"input": ("*", {})
"input": (IO.ANY, {})
}
}
@@ -100,15 +88,11 @@ class CastToBoolean:
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "convert_to_boolean"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert_to_boolean(self, input: Any) -> tuple[bool]:
return (bool(input),)
class CastToList:
class CastToList(ComfyNodeABC):
"""
Converts any input to a LIST. Non-list inputs are wrapped in a list. If input is a ComfyUI data list,
it converts the individual items into a Python LIST.
@@ -117,7 +101,7 @@ class CastToList:
def INPUT_TYPES(cls):
return {
"required": {
"input": ("*", {})
"input": (IO.ANY, {})
}
}
@@ -126,17 +110,13 @@ class CastToList:
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "convert_to_list"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert_to_list(self, input: Any) -> tuple[list]:
if isinstance(input, list):
return (input,)
return ([input],)
class CastDataListToList:
class CastDataListToList(ComfyNodeABC):
"""
Converts a ComfyUI data list into a LIST object.
@@ -147,7 +127,7 @@ class CastDataListToList:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*", {}),
"list": (IO.ANY, {}),
}
}
@@ -157,15 +137,11 @@ class CastDataListToList:
FUNCTION = "convert"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert(self, **kwargs: list[Any]) -> tuple[list[Any]]:
return (kwargs.get('list', []).copy(),)
class CastToSet:
class CastToSet(ComfyNodeABC):
"""
Converts any input to a SET. Non-set inputs are converted into a set. If input is a ComfyUI data list,
it casts the individual items into a SET.
@@ -174,7 +150,7 @@ class CastToSet:
def INPUT_TYPES(cls):
return {
"required": {
"input": ("*", {})
"input": (IO.ANY, {})
}
}
@@ -183,17 +159,13 @@ class CastToSet:
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "convert_to_set"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert_to_set(self, input: Any) -> tuple[set]:
if isinstance(input, set):
return (input,)
return ({input,} if not isinstance(input, list) else set(input),)
class CastDataListToSet:
class CastDataListToSet(ComfyNodeABC):
"""
Converts a ComfyUI data list into a LIST object.
@@ -204,7 +176,7 @@ class CastDataListToSet:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*", {}),
"list": (IO.ANY, {}),
}
}
@@ -214,15 +186,11 @@ class CastDataListToSet:
FUNCTION = "convert"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert(self, **kwargs: list[Any]) -> tuple[set[Any]]:
return (set(kwargs.get('list', [])),)
class CastToDict:
class CastToDict(ComfyNodeABC):
"""
Converts compatible inputs to a DICT. Input must be a mapping or a list of key-value pairs.
"""
@@ -230,7 +198,7 @@ class CastToDict:
def INPUT_TYPES(cls):
return {
"required": {
"input": ("*", {})
"input": (IO.ANY, {})
}
}
@@ -239,10 +207,6 @@ class CastToDict:
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "convert_to_dict"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def convert_to_dict(self, input: Any) -> tuple[dict]:
try:
return (dict(input),)
+26 -34
View File
@@ -1,7 +1,8 @@
from typing import Any
from inspect import cleandoc
from comfy.comfy_types.node_typing import IO, ComfyNodeABC
class IfElse:
class IfElse(ComfyNodeABC):
"""
Implements a conditional branch (if/else) in the workflow.
@@ -13,24 +14,18 @@ class IfElse:
def INPUT_TYPES(cls):
return {
"required": {
"condition": ("BOOLEAN", {}),
"if_true": ("*", {"lazy": True}),
"if_false": ("*", {"lazy": True}),
"condition": (IO.BOOLEAN, {}),
"if_true": (IO.ANY, {"lazy": True}),
"if_false": (IO.ANY, {"lazy": True}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("result",)
CATEGORY = "Basic/Flow Control"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "execute"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool|str:
if input_types["condition"] not in ("BOOLEAN"):
return "condition must be a BOOLEAN type"
return True
def check_lazy_status(self, condition: bool, if_true: Any, if_false: Any) -> list[str]:
needed = []
if if_true is None and condition is True:
@@ -43,7 +38,7 @@ class IfElse:
return (if_true if condition else if_false,)
class SwitchCase:
class SwitchCase(ComfyNodeABC):
"""
Implements a switch/case selection in the workflow.
@@ -54,48 +49,45 @@ class SwitchCase:
NOTE: This version of the node will most likely be deprecated in the future.
"""
EXPERIMENTAL = True
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"selector": ("INT", {"default": 0, "min": 0}),
"case_0": ("*", {"lazy": True}),
"case_1": ("*", {"lazy": True}),
"selector": (IO.INT, {"default": 0, "min": 0}),
"case_0": (IO.ANY, {"lazy": True}),
"case_1": (IO.ANY, {"lazy": True}),
},
"optional": {
"case_2": ("*", {"lazy": True}),
"case_3": ("*", {"lazy": True}),
"case_4": ("*", {"lazy": True}),
"default": ("*", {"lazy": True}),
"case_2": (IO.ANY, {"lazy": True}),
"case_3": (IO.ANY, {"lazy": True}),
"case_4": (IO.ANY, {"lazy": True}),
"default": (IO.ANY, {"lazy": True}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("result",)
CATEGORY = "Basic/Flow Control"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "execute"
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool|str:
if input_types["selector"] not in ("INT"):
return "selector must be a INT type"
return True
def check_lazy_status(self, condition: int, case_0: Any, case_1: Any,
case_2: Any, case_3: Any, case_4: Any, default: Any) -> list[str]:
def check_lazy_status(self, selector: int, case_0: Any, case_1: Any,
case_2: Any = None, case_3: Any = None,
case_4: Any = None, default: Any = None) -> list[str]:
needed = []
if case_0 is None and condition == 0:
if case_0 is None and selector == 0:
needed.append("case_0")
if case_1 is None and condition == 1:
if case_1 is None and selector == 1:
needed.append("case_1")
if case_2 is None and condition == 2:
if case_2 is None and selector == 2:
needed.append("case_2")
if case_3 is None and condition == 3:
if case_3 is None and selector == 3:
needed.append("case_3")
if case_4 is None and condition == 4:
if case_4 is None and selector == 4:
needed.append("case_4")
if default is None and not 0 <= condition <= 4:
if default is None and not 0 <= selector <= 4:
needed.append("default")
return needed
+80 -225
View File
@@ -1,8 +1,8 @@
from typing import Any
from inspect import cleandoc
from comfy.comfy_types.node_typing import IO, ComfyNodeABC
class DataListAppend:
class DataListAppend(ComfyNodeABC):
"""
Adds an item to the end of a list.
@@ -13,12 +13,12 @@ class DataListAppend:
def INPUT_TYPES(cls):
return {
"optional": {
"list":("*",{}),
"item":("*",{}),
"list":(IO.ANY,{}),
"item":(IO.ANY,{}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -26,10 +26,6 @@ class DataListAppend:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def append(self, **kwargs: list[Any]) -> tuple[list[Any]]:
result = kwargs.get('list', []).copy()
item = kwargs.get('item', [])
@@ -38,7 +34,7 @@ class DataListAppend:
return (result,)
class DataListExtend:
class DataListExtend(ComfyNodeABC):
"""
Extends a list by appending elements from another list.
@@ -49,12 +45,12 @@ class DataListExtend:
def INPUT_TYPES(cls):
return {
"optional": {
"list_a": ("*",{}),
"list_b": ("*",{}),
"list_a": (IO.ANY,{}),
"list_b": (IO.ANY,{}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -62,15 +58,11 @@ class DataListExtend:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def extend(self, **kwargs: list[Any]) -> tuple[list[Any]]:
return (kwargs.get('list_a', []) + kwargs.get('list_b', []),)
class DataListInsert:
class DataListInsert(ComfyNodeABC):
"""
Inserts an item at a specified position in a list.
@@ -81,13 +73,13 @@ class DataListInsert:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"index": ("INT", {"default": 0}),
"item": ("*",),
"list": (IO.ANY,),
"index": (IO.INT, {"default": 0}),
"item": (IO.ANY,),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -95,17 +87,13 @@ class DataListInsert:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def insert(self, **kwargs: list[Any]) -> tuple[list[Any]]:
result = kwargs.get('list', []).copy()
result.insert(kwargs.get('index', [0])[0], kwargs.get('item', [None])[0])
return (result,)
class DataListRemove:
class DataListRemove(ComfyNodeABC):
"""
Removes the first occurrence of a specified value from a list.
@@ -116,12 +104,12 @@ class DataListRemove:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"value": ("*",),
"list": (IO.ANY,),
"value": (IO.ANY,),
}
}
RETURN_TYPES = ("*", "BOOLEAN",)
RETURN_TYPES = (IO.ANY, IO.BOOLEAN,)
RETURN_NAMES = ("list", "success",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -129,10 +117,6 @@ class DataListRemove:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True, False,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def remove(self, **kwargs: list[Any]) -> tuple[list[Any], bool]:
result = kwargs.get('list', []).copy()
value = kwargs.get('value', [])
@@ -143,7 +127,7 @@ class DataListRemove:
return result, False
class DataListPop:
class DataListPop(ComfyNodeABC):
"""
Removes and returns an item at a specified position in a list.
@@ -156,14 +140,14 @@ class DataListPop:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"list": (IO.ANY,),
},
"optional": {
"index": ("INT", {"default": -1}),
"index": (IO.INT, {"default": -1}),
}
}
RETURN_TYPES = ("*", "*")
RETURN_TYPES = (IO.ANY, IO.ANY)
RETURN_NAMES = ("list", "item")
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -171,12 +155,6 @@ class DataListPop:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True, False)
@classmethod
def VALIDATE_INPUTS(cls, input_types: list[dict[str, str]]) -> bool|str:
if input_types[0].get("index", "INT") != "INT":
return "index must be an INT type"
return True
def pop(self, **kwargs: list[Any]) -> tuple[list[Any], Any]:
result = kwargs.get('list', []).copy()
index = kwargs.get('index', [-1])[0]
@@ -187,38 +165,7 @@ class DataListPop:
return result, None
class DataListClear:
"""
Removes all items from a list.
This node takes a list as input and returns a new empty list.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"optional": {
"list": ("*",),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "clear"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def clear(self, **kwargs: list[Any]) -> tuple[list]:
# Return a new empty list rather than modifying the input
return ([],)
class DataListIndex:
class DataListIndex(ComfyNodeABC):
"""
Returns the index of the first occurrence of a value in a list.
@@ -230,26 +177,22 @@ class DataListIndex:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"value": ("*",),
"list": (IO.ANY,),
"value": (IO.ANY,),
},
"optional": {
"start": ("INT", {"default": 0}),
"end": ("INT", {"default": -1}),
"start": (IO.INT, {"default": 0}),
"end": (IO.INT, {"default": -1}),
}
}
RETURN_TYPES = ("INT",)
RETURN_TYPES = (IO.INT,)
RETURN_NAMES = ("index",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "list_index"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def list_index(self, **kwargs: list[Any]) -> tuple[int]:
input_list = kwargs.get('list', [])
value = kwargs.get('value', [None])[0]
@@ -264,7 +207,7 @@ class DataListIndex:
return (-1,)
class DataListCount:
class DataListCount(ComfyNodeABC):
"""
Counts the number of occurrences of a value in a list.
@@ -275,28 +218,24 @@ class DataListCount:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"value": ("*",),
"list": (IO.ANY,),
"value": (IO.ANY,),
}
}
RETURN_TYPES = ("INT",)
RETURN_TYPES = (IO.INT,)
RETURN_NAMES = ("count",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "count"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def count(self, **kwargs: list[Any]) -> tuple[int]:
value = kwargs.get('value', [None])[0]
return (kwargs.get('list', []).count(value),)
class DataListSort:
class DataListSort(ComfyNodeABC):
"""
Sorts the items in a list.
@@ -307,14 +246,14 @@ class DataListSort:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"list": (IO.ANY,),
},
"optional": {
"reverse": (["False", "True"], {"default": "False"}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -322,10 +261,6 @@ class DataListSort:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def sort(self, **kwargs: list[Any]) -> tuple[list[Any]]:
# Convert string to boolean
reverse = kwargs.get('reverse', ["False"])[0] == "True"
@@ -334,7 +269,7 @@ class DataListSort:
return (result,)
class DataListReverse:
class DataListReverse(ComfyNodeABC):
"""
Reverses the order of items in a list.
@@ -344,11 +279,11 @@ class DataListReverse:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"list": (IO.ANY,),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -356,47 +291,13 @@ class DataListReverse:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def reverse(self, **kwargs: list[Any]) -> tuple[list[Any]]:
result = kwargs.get('list', []).copy()
result.reverse()
return (result,)
class DataListCopy:
"""
Creates a shallow copy of a list.
This node takes a list as input and returns a new list that is a shallow copy of the original.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "copy"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def copy(self, **kwargs: list[Any]) -> tuple[list[Any]]:
return (kwargs.get('list', []).copy(),)
class DataListLength:
class DataListLength(ComfyNodeABC):
"""
Counts the number of items in a list.
@@ -406,26 +307,22 @@ class DataListLength:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"list": (IO.ANY,),
}
}
RETURN_TYPES = ("INT",)
RETURN_TYPES = (IO.INT,)
RETURN_NAMES = ("length",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "length"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def length(self, **kwargs: list[Any]) -> tuple[int]:
return (len(kwargs.get('list', [])),)
class DataListSlice:
class DataListSlice(ComfyNodeABC):
"""
Creates a slice of a list.
@@ -436,16 +333,16 @@ class DataListSlice:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"list": (IO.ANY,),
},
"optional": {
"start": ("INT", {"default": 0}),
"stop": ("INT", {"default": -1}),
"step": ("INT", {"default": 1}),
"start": (IO.INT, {"default": 0}),
"stop": (IO.INT, {"default": -1}),
"step": (IO.INT, {"default": 1}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -453,10 +350,6 @@ class DataListSlice:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def slice(self, **kwargs: list[Any]) -> tuple[list[Any]]:
input_list = kwargs.get('list', [])
start = kwargs.get('start', [0])[0]
@@ -469,7 +362,7 @@ class DataListSlice:
return (input_list[start:stop:step],)
class DataListGetItem:
class DataListGetItem(ComfyNodeABC):
"""
Retrieves an item at a specified position in a list.
@@ -481,22 +374,18 @@ class DataListGetItem:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"index": ("INT", {"default": 0}),
"list": (IO.ANY,),
"index": (IO.INT, {"default": 0}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("item",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "get_item"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def get_item(self, **kwargs: list[Any]) -> tuple[Any]:
index = kwargs.get('index', [0])[0]
try:
@@ -505,7 +394,7 @@ class DataListGetItem:
return (None,)
class DataListSetItem:
class DataListSetItem(ComfyNodeABC):
"""
Sets an item at a specified position in a list.
@@ -516,13 +405,13 @@ class DataListSetItem:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"index": ("INT", {"default": 0}),
"value": ("*",),
"list": (IO.ANY,),
"index": (IO.INT, {"default": 0}),
"value": (IO.ANY,),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -530,10 +419,6 @@ class DataListSetItem:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def set_item(self, **kwargs: list[Any]) -> tuple[Any]:
input_list = kwargs.get('list', [])
index = kwargs.get('index', [0])[0]
@@ -546,7 +431,7 @@ class DataListSetItem:
raise IndexError(f"Index {index} out of range for list of length {len(input_list)}")
class DataListContains:
class DataListContains(ComfyNodeABC):
"""
Checks if a list contains a specified value.
@@ -557,22 +442,18 @@ class DataListContains:
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"value": ("*",),
"list": (IO.ANY,),
"value": (IO.ANY,),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_TYPES = (IO.BOOLEAN,)
RETURN_NAMES = ("contains",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "contains"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def contains(self, **kwargs: list[Any]) -> tuple[bool]:
value = kwargs.get('value', [])
if len(value) == 0:
@@ -580,7 +461,7 @@ class DataListContains:
return (value[0] in kwargs.get('list', []),)
class DataListCreateEmpty:
class DataListCreateEmpty(ComfyNodeABC):
"""
Creates a new empty list.
@@ -590,7 +471,7 @@ class DataListCreateEmpty:
def INPUT_TYPES(cls):
return {"required": {}}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -601,7 +482,7 @@ class DataListCreateEmpty:
return ([],)
class DataListZip:
class DataListZip(ComfyNodeABC):
"""
Combines multiple lists element-wise.
@@ -613,16 +494,16 @@ class DataListZip:
def INPUT_TYPES(cls):
return {
"required": {
"list1": ("*",),
"list2": ("*",),
"list1": (IO.ANY,),
"list2": (IO.ANY,),
},
"optional": {
"list3": ("*",),
"list4": ("*",),
"list3": (IO.ANY,),
"list4": (IO.ANY,),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -630,10 +511,6 @@ class DataListZip:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
return True
def zip_lists(self, **kwargs: list[Any]) -> tuple[list[Any]]:
lists = [kwargs.get('list1', []), kwargs.get('list2', [])]
@@ -648,7 +525,7 @@ class DataListZip:
return (result,)
class DataListFilter:
class DataListFilter(ComfyNodeABC):
"""
Filters a data list using boolean values.
@@ -663,12 +540,12 @@ class DataListFilter:
def INPUT_TYPES(cls):
return {
"required": {
"value": ("*", {}),
"filter": ("BOOLEAN", {"forceInput": True}),
"value": (IO.ANY, {}),
"filter": (IO.BOOLEAN, {"forceInput": True}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("filtered_list",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -676,12 +553,6 @@ class DataListFilter:
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
@classmethod
def VALIDATE_INPUTS(cls, input_types: list[dict[str, str]]) -> bool|str:
if input_types[0]["filter"] != "BOOLEAN":
return "filter must be a BOOLEAN type"
return True
def filter_data(self, **kwargs: list[Any]) -> tuple[list[Any]]:
values = kwargs.get('value', [])
filters = kwargs.get('filter', [])
@@ -692,7 +563,7 @@ class DataListFilter:
return (result,)
class DataListMin:
class DataListMin(ComfyNodeABC):
"""
Finds the minimum value in a list of numbers.
@@ -704,23 +575,17 @@ class DataListMin:
def INPUT_TYPES(cls):
return {
"required": {
"list": (["FLOAT", "INT"], {}),
"list": (IO.NUMBER, {}),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("min_value",)
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("min",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "find_min"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: list[dict[str, str]]) -> bool|str:
if input_types[0]["list"] not in ("FLOAT", "INT"):
return "list must contain FLOAT or INT types"
return True
def find_min(self, **kwargs: list[Any]) -> tuple[Any]:
values = kwargs.get('list', [])
if not values:
@@ -734,7 +599,7 @@ class DataListMin:
return (None,)
class DataListMax:
class DataListMax(ComfyNodeABC):
"""
Finds the maximum value in a list of numbers.
@@ -746,23 +611,17 @@ class DataListMax:
def INPUT_TYPES(cls):
return {
"required": {
"list": (["FLOAT", "INT"], {}),
"list": (IO.NUMBER, {}),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("max_value",)
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("max",)
CATEGORY = "Basic/data list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "find_max"
INPUT_IS_LIST = True
@classmethod
def VALIDATE_INPUTS(cls, input_types: list[dict[str, str]]) -> bool|str:
if input_types[0]["list"] not in ("FLOAT", "INT"):
return "list must contain FLOAT or INT types"
return True
def find_max(self, **kwargs: list[Any]) -> tuple[Any]:
values = kwargs.get('list', [])
if not values:
@@ -782,12 +641,10 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: DataListInsert": DataListInsert,
"Basic data handling: DataListRemove": DataListRemove,
"Basic data handling: DataListPop": DataListPop,
"Basic data handling: DataListClear": DataListClear,
"Basic data handling: DataListIndex": DataListIndex,
"Basic data handling: DataListCount": DataListCount,
"Basic data handling: DataListSort": DataListSort,
"Basic data handling: DataListReverse": DataListReverse,
"Basic data handling: DataListCopy": DataListCopy,
"Basic data handling: DataListLength": DataListLength,
"Basic data handling: DataListSlice": DataListSlice,
"Basic data handling: DataListGetItem": DataListGetItem,
@@ -806,12 +663,10 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: DataListInsert": "insert",
"Basic data handling: DataListRemove": "remove",
"Basic data handling: DataListPop": "pop",
"Basic data handling: DataListClear": "clear",
"Basic data handling: DataListIndex": "index",
"Basic data handling: DataListCount": "count",
"Basic data handling: DataListSort": "sort",
"Basic data handling: DataListReverse": "reverse",
"Basic data handling: DataListCopy": "copy",
"Basic data handling: DataListLength": "length",
"Basic data handling: DataListSlice": "slice",
"Basic data handling: DataListGetItem": "get item",
+84 -151
View File
@@ -1,7 +1,8 @@
from typing import Any
from inspect import cleandoc
from comfy.comfy_types.node_typing import IO, ComfyNodeABC
class DictCreate:
class DictCreate(ComfyNodeABC):
"""
Creates a new empty dictionary.
@@ -20,7 +21,7 @@ class DictCreate:
return ({},)
class DictCreateFromItems:
class DictCreateFromItemsList(ComfyNodeABC):
"""
Creates a dictionary from a list of key-value pairs.
@@ -43,7 +44,8 @@ class DictCreateFromItems:
try:
# Check if items are valid (key-value pairs)
for item in items:
if not isinstance(item, tuple) or len(item) != 2:
print(f'item: "{item}", len(item): {len(item)}, items: "{items}", len(items): {len(items)}')
if not isinstance(item, tuple) and len(item) != 2:
raise ValueError("Each item must be a (key, value) pair")
return (dict(items),)
@@ -51,7 +53,41 @@ class DictCreateFromItems:
raise ValueError(f"Error creating dictionary from items: {str(e)}")
class DictGet:
class DictCreateFromItemsDataList(ComfyNodeABC):
"""
Creates a dictionary from a list of key-value pairs.
This node takes a list of key-value pairs (tuples) and builds a dictionary from them.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"item": (IO.ANY, {}),
}
}
RETURN_TYPES = ("DICT",)
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "create_from_items"
INPUT_IS_LIST = True
def create_from_items(self, **kwargs: list[Any]) -> tuple[dict]:
try:
# Check if items are valid (key-value pairs)
items = kwargs.get('item', [])
for item in items:
print(f'item: "{item}", len(item): {len(item)}, items: "{items}", len(items): {len(items)}')
if not isinstance(item, tuple) and len(item) != 2:
raise ValueError("Each item must be a (key, value) pair")
return (dict(items),)
except Exception as e:
raise ValueError(f"Error creating dictionary from items: {str(e)}")
class DictGet(ComfyNodeABC):
"""
Retrieves a value from a dictionary using the specified key.
@@ -64,14 +100,14 @@ class DictGet:
return {
"required": {
"input_dict": ("DICT", {}),
"key": ("STRING", {"default": ""}),
"key": (IO.STRING, {"default": ""}),
},
"optional": {
"default": ("*", {}),
"default": (IO.ANY, {}),
}
}
RETURN_TYPES = ("*",)
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("value",)
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -81,7 +117,7 @@ class DictGet:
return (input_dict.get(key, default),)
class DictSet:
class DictSet(ComfyNodeABC):
"""
Adds or updates a key-value pair in a dictionary.
@@ -93,8 +129,8 @@ class DictSet:
return {
"required": {
"input_dict": ("DICT", {}),
"key": ("STRING", {"default": ""}),
"value": ("*", {}),
"key": (IO.STRING, {"default": ""}),
"value": (IO.ANY, {}),
}
}
@@ -109,7 +145,7 @@ class DictSet:
return (result,)
class DictKeys:
class DictKeys(ComfyNodeABC):
"""
Returns all keys in a dictionary.
@@ -132,7 +168,7 @@ class DictKeys:
return (list(input_dict.keys()),)
class DictValues:
class DictValues(ComfyNodeABC):
"""
Returns all values in a dictionary.
@@ -155,7 +191,7 @@ class DictValues:
return (list(input_dict.values()),)
class DictItems:
class DictItems(ComfyNodeABC):
"""
Returns all key-value pairs in a dictionary.
@@ -179,7 +215,7 @@ class DictItems:
return (list(input_dict.items()),)
class DictContainsKey:
class DictContainsKey(ComfyNodeABC):
"""
Checks if a key exists in a dictionary.
@@ -191,11 +227,11 @@ class DictContainsKey:
return {
"required": {
"input_dict": ("DICT", {}),
"key": ("STRING", {"default": ""}),
"key": (IO.STRING, {"default": ""}),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_TYPES = (IO.BOOLEAN,)
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "contains_key"
@@ -204,54 +240,7 @@ class DictContainsKey:
return (key in input_dict,)
class DictClear:
"""
Removes all items from a dictionary.
This node takes a dictionary as input and returns a new empty dictionary.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_dict": ("DICT", {}),
}
}
RETURN_TYPES = ("DICT",)
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "clear"
def clear(self, input_dict: dict) -> tuple[dict]:
return ({},)
class DictCopy:
"""
Creates a shallow copy of a dictionary.
This node takes a dictionary as input and returns a new dictionary that is
a shallow copy of the original.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_dict": ("DICT", {}),
}
}
RETURN_TYPES = ("DICT",)
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "copy"
def copy(self, input_dict: dict) -> tuple[dict]:
return (input_dict.copy(),)
class DictFromKeys:
class DictFromKeys(ComfyNodeABC):
"""
Creates a dictionary from a list of keys and a default value.
@@ -266,7 +255,7 @@ class DictFromKeys:
"keys": ("LIST", {}),
},
"optional": {
"value": ("*", {}),
"value": (IO.ANY, {}),
}
}
@@ -279,7 +268,7 @@ class DictFromKeys:
return (dict.fromkeys(keys, value),)
class DictPop:
class DictPop(ComfyNodeABC):
"""
Removes and returns a key-value pair from a dictionary.
@@ -293,14 +282,14 @@ class DictPop:
return {
"required": {
"input_dict": ("DICT", {}),
"key": ("STRING", {"default": ""}),
"key": (IO.STRING, {"default": ""}),
},
"optional": {
"default_value": ("*", {}),
"default_value": (IO.ANY, {}),
}
}
RETURN_TYPES = ("DICT", "*")
RETURN_TYPES = ("DICT", IO.ANY)
RETURN_NAMES = ("dict", "value")
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -319,7 +308,7 @@ class DictPop:
raise ValueError(f"Error popping key from dictionary: {str(e)}")
class DictPopItem:
class DictPopItem(ComfyNodeABC):
"""
Removes and returns an arbitrary key-value pair from a dictionary.
@@ -335,7 +324,7 @@ class DictPopItem:
}
}
RETURN_TYPES = ("DICT", "STRING", "*", "BOOLEAN")
RETURN_TYPES = ("DICT", IO.STRING, IO.ANY, IO.BOOLEAN)
RETURN_NAMES = ("dict", "key", "value", "success")
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -353,7 +342,7 @@ class DictPopItem:
return result, "", None, False
class DictSetDefault:
class DictSetDefault(ComfyNodeABC):
"""
Returns the value for a key, setting a default if the key doesn't exist.
@@ -366,12 +355,12 @@ class DictSetDefault:
return {
"required": {
"input_dict": ("DICT", {}),
"key": ("STRING", {"default": ""}),
"default_value": ("*", {}),
"key": (IO.STRING, {"default": ""}),
"default_value": (IO.ANY, {}),
}
}
RETURN_TYPES = ("DICT", "*")
RETURN_TYPES = ("DICT", IO.ANY)
RETURN_NAMES = ("DICT", "value")
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -383,7 +372,7 @@ class DictSetDefault:
return result, value
class DictUpdate:
class DictUpdate(ComfyNodeABC):
"""
Updates a dictionary with key-value pairs from another dictionary.
@@ -411,7 +400,7 @@ class DictUpdate:
return (result,)
class DictLength:
class DictLength(ComfyNodeABC):
"""
Returns the number of key-value pairs in a dictionary.
@@ -435,7 +424,7 @@ class DictLength:
return (len(input_dict),)
class DictMerge:
class DictMerge(ComfyNodeABC):
"""
Merges multiple dictionaries into a single dictionary.
@@ -475,7 +464,7 @@ class DictMerge:
return (result,)
class DictGetKeysValues:
class DictGetKeysValues(ComfyNodeABC):
"""
Returns keys and values as separate lists.
@@ -502,7 +491,7 @@ class DictGetKeysValues:
return keys, values
class DictRemove:
class DictRemove(ComfyNodeABC):
"""
Removes a key-value pair from a dictionary.
@@ -515,11 +504,11 @@ class DictRemove:
return {
"required": {
"input_dict": ("DICT", {}),
"key": ("STRING", {"default": ""}),
"key": (IO.STRING, {"default": ""}),
}
}
RETURN_TYPES = ("DICT", "BOOLEAN")
RETURN_TYPES = ("DICT", IO.BOOLEAN)
RETURN_NAMES = ("dict", "key_removed")
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -533,7 +522,7 @@ class DictRemove:
return result, False
class DictFilterByKeys:
class DictFilterByKeys(ComfyNodeABC):
"""
Creates a new dictionary with only the specified keys.
@@ -559,7 +548,7 @@ class DictFilterByKeys:
return (result,)
class DictExcludeKeys:
class DictExcludeKeys(ComfyNodeABC):
"""
Creates a new dictionary excluding the specified keys.
@@ -585,7 +574,7 @@ class DictExcludeKeys:
return (result,)
class DictGetMultiple:
class DictGetMultiple(ComfyNodeABC):
"""
Retrieves multiple values from a dictionary using a list of keys.
@@ -601,7 +590,7 @@ class DictGetMultiple:
"keys": ("LIST", {}),
},
"optional": {
"default": ("*", {}),
"default": (IO.ANY, {}),
}
}
@@ -616,7 +605,7 @@ class DictGetMultiple:
return (values,)
class DictInvert:
class DictInvert(ComfyNodeABC):
"""
Creates a new dictionary with keys and values swapped.
@@ -632,7 +621,7 @@ class DictInvert:
}
}
RETURN_TYPES = ("DICT", "BOOLEAN")
RETURN_TYPES = ("DICT", IO.BOOLEAN)
RETURN_NAMES = ("inverted_dict", "success")
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -647,7 +636,7 @@ class DictInvert:
return input_dict, False
class DictCreateFromLists:
class DictCreateFromLists(ComfyNodeABC):
"""
Creates a dictionary from separate lists of keys and values.
@@ -675,7 +664,7 @@ class DictCreateFromLists:
return (result,)
class DictCompare:
class DictCompare(ComfyNodeABC):
"""
Compares two dictionaries and reports differences.
@@ -692,7 +681,7 @@ class DictCompare:
}
}
RETURN_TYPES = ("BOOLEAN", "LIST", "LIST", "LIST")
RETURN_TYPES = (IO.BOOLEAN, "LIST", "LIST", "LIST")
RETURN_NAMES = ("are_equal", "only_in_dict1", "only_in_dict2", "different_values")
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
@@ -715,69 +704,16 @@ class DictCompare:
return are_equal, only_in_dict1, only_in_dict2, different_values
class AnyToDict:
"""
Converts compatible data structures to a dictionary.
This node attempts to convert an input value to a dictionary. Compatible
inputs include sequences of key-value pairs, mapping objects, and objects
with a to_dict() or as_dict() method.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input": ("*", {}),
}
}
RETURN_TYPES = ("DICT", "BOOLEAN")
CATEGORY = "Basic/DICT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "convert"
def convert(self, input: Any) -> tuple[dict]:
try:
if isinstance(input, dict):
return (input.copy(),)
# Try converting from items
if hasattr(input, "items"):
return (dict(input.items()),)
# Try converting from sequence of pairs
if hasattr(input, "__iter__") and not isinstance(input, str):
try:
result = dict(input)
return (result,)
except (TypeError, ValueError):
pass
# Check for to_dict or as_dict methods
if hasattr(input, "to_dict") and callable(getattr(input, "to_dict")):
return (input.to_dict(),)
if hasattr(input, "as_dict") and callable(getattr(input, "as_dict")):
return (input.as_dict(),)
# Failed to convert
return ({},)
except Exception:
return ({},)
NODE_CLASS_MAPPINGS = {
"Basic data handling: DictCreate": DictCreate,
"Basic data handling: DictCreateFromItems": DictCreateFromItems,
"Basic data handling: DictCreateFromItemsList": DictCreateFromItemsList,
"Basic data handling: DictCreateFromItemsDataList": DictCreateFromItemsDataList,
"Basic data handling: DictGet": DictGet,
"Basic data handling: DictSet": DictSet,
"Basic data handling: DictKeys": DictKeys,
"Basic data handling: DictValues": DictValues,
"Basic data handling: DictItems": DictItems,
"Basic data handling: DictContainsKey": DictContainsKey,
"Basic data handling: DictClear": DictClear,
"Basic data handling: DictCopy": DictCopy,
"Basic data handling: DictFromKeys": DictFromKeys,
"Basic data handling: DictPop": DictPop,
"Basic data handling: DictPopItem": DictPopItem,
@@ -793,20 +729,18 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: DictInvert": DictInvert,
"Basic data handling: DictCreateFromLists": DictCreateFromLists,
"Basic data handling: DictCompare": DictCompare,
"Basic data handling: AnyToDict": AnyToDict,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: DictCreate": "create",
"Basic data handling: DictCreateFromItems": "from_items",
"Basic data handling: DictCreateFromItemsList": "from items (LIST)",
"Basic data handling: DictCreateFromItemsDataList": "from items (data list)",
"Basic data handling: DictGet": "get",
"Basic data handling: DictSet": "set",
"Basic data handling: DictKeys": "keys",
"Basic data handling: DictValues": "values",
"Basic data handling: DictItems": "items",
"Basic data handling: DictContainsKey": "contains_key",
"Basic data handling: DictClear": "clear",
"Basic data handling: DictCopy": "copy",
"Basic data handling: DictFromKeys": "fromkeys",
"Basic data handling: DictPop": "pop",
"Basic data handling: DictPopItem": "popitem",
@@ -822,5 +756,4 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: DictInvert": "invert",
"Basic data handling: DictCreateFromLists": "from_lists",
"Basic data handling: DictCompare": "compare",
"Basic data handling: AnyToDict": "any_to_DICT",
}