bump version to 2.0.0

- Critical change that ensures reliablity and **should** have no bad side
  effects, but when you see any, please report:
  DICT, LIST, SET and Data List nodes (plus the DICT/LIST-taking STRING nodes)
  now degrade gracefully when their data source is not connected (`None`) instead
  of raising: read/query nodes return their natural "no data" result (the `get`
  default, an empty list, `False`, `0`, `None`), and transform nodes behave as if
  given an empty container.
- Add a "continue if not empty" flow-control node: it passes a value through
  unchanged when it is non-empty, and blocks execution (via an execution blocker)
  when the value is empty. It is list-aware (`INPUT_IS_LIST` / `OUTPUT_IS_LIST`),
  so it is immune to ComfyUI's empty-input batching crash and passes data lists
  through unchanged. By default the block is silent; an optional `message` can be
  set to show a dialog. Use it before nodes that cannot handle an empty input
  (e.g. a node receiving an empty Data List) to stop execution instead of failing.
This commit is contained in:
StableLlama
2026-09-11 11:02:43 +02:00
parent 8ff49ec895
commit 1deac10f15
15 changed files with 391 additions and 60 deletions
@@ -213,6 +213,65 @@ class SwitchCase(ComfyNodeABC):
return (kwargs.get("default"),)
class ContinueIfNotEmpty(ComfyNodeABC):
"""
Passes a value through only when it is not empty; otherwise blocks execution.
This node guards against feeding an empty collection (None, empty data list,
empty LIST/DICT/SET, etc.) into downstream nodes that cannot handle an empty
input — e.g. ComfyUI's own list-mapping fails with an empty input list.
When the value is non-empty it is passed through unchanged; when it is empty an
execution blocker is returned so downstream nodes do not run. It is especially
useful placed before a node such as "get" / "filter" / "list" that would
otherwise crash on an empty input (for example an empty Data List).
A value counts as empty when it is falsy: ``None``, ``[]``, ``{}``, ``set()``,
``""``, ``0`` or ``False``.
By default the block is silent; set ``message`` to show a dialog (like
"continue flow") when the execution is blocked.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"value": (IO.ANY, {"tooltip": "The value to guard; execution is blocked when the value is empty."}),
},
"optional": {
"message": (IO.STRING, {"default": "", "tooltip": "Optional message shown when the flow is blocked. Leave empty for silent operation."}),
}
}
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("value",)
OUTPUT_TOOLTIPS = ("The input value, or an execution blocker when the value is empty.",)
CATEGORY = "Basic/flow control"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "execute"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
def execute(self, value, message="") -> tuple[Any]:
# INPUT_IS_LIST hands the raw inputs over (a data list or a single value),
# so ComfyUI never slices them — this node is immune to the empty-list
# batching crash it guards against. `message` arrives as a list under
# ComfyUI, but may be a plain string when called directly.
if value is None:
value = []
msg = message[0] if isinstance(message, (list, tuple)) and message else (message or "")
if isinstance(value, list):
empty = (len(value) == 0)
else:
empty = (not value)
if empty:
return (ExecutionBlocker(msg if msg else None),)
# OUTPUT_IS_LIST expects a list, so wrap a single non-list value.
if not isinstance(value, list):
value = [value]
return (value,)
class ContinueFlow(ComfyNodeABC):
"""
Conditionally enable or disable a flow.
@@ -380,6 +439,7 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: IfElse": IfElse,
"Basic data handling: IfElifElse": IfElifElse,
"Basic data handling: SwitchCase": SwitchCase,
"Basic data handling: ContinueIfNotEmpty": ContinueIfNotEmpty,
"Basic data handling: ContinueFlow": ContinueFlow,
"Basic data handling: FlowSelect": FlowSelect,
"Basic data handling: ForceCalculation": ForceCalculation,
@@ -391,6 +451,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: IfElse": "if/else",
"Basic data handling: IfElifElse": "if/elif/.../else",
"Basic data handling: SwitchCase": "switch/case",
"Basic data handling: ContinueIfNotEmpty": "continue if not empty",
"Basic data handling: ContinueFlow": "continue flow",
"Basic data handling: FlowSelect": "flow select",
"Basic data handling: ForceCalculation": "force calculation",
+52 -52
View File
@@ -212,7 +212,7 @@ class DataListAll(ComfyNodeABC):
INPUT_IS_LIST = True
def check_all(self, **kwargs: list[Any]) -> tuple[bool]:
return (all(kwargs.get('list', [])),)
return (all(kwargs.get('list') or []),)
class DataListAny(ComfyNodeABC):
@@ -238,7 +238,7 @@ class DataListAny(ComfyNodeABC):
INPUT_IS_LIST = True
def check_any(self, **kwargs: list[Any]) -> tuple[bool]:
return (any(kwargs.get('list', [])),)
return (any(kwargs.get('list') or []),)
class DataListAppend(ComfyNodeABC):
@@ -267,8 +267,8 @@ class DataListAppend(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def append(self, **kwargs: list[Any]) -> tuple[list[Any]]:
result = kwargs.get('list', []).copy()
item = kwargs.get('item', [])
result = (kwargs.get('list') or []).copy()
item = kwargs.get('item') or []
if len(item) > 0:
result.append(item[0])
return (result,)
@@ -299,10 +299,10 @@ class DataListContains(ComfyNodeABC):
INPUT_IS_LIST = True
def contains(self, **kwargs: list[Any]) -> tuple[bool]:
value = kwargs.get('value', [])
value = kwargs.get('value') or []
if len(value) == 0:
return (False,)
return (value[0] in kwargs.get('list', []),)
return (value[0] in (kwargs.get('list') or []),)
class DataListCount(ComfyNodeABC):
@@ -330,8 +330,8 @@ class DataListCount(ComfyNodeABC):
INPUT_IS_LIST = True
def count(self, **kwargs: list[Any]) -> tuple[int]:
value = kwargs.get('value', [None])[0]
return (kwargs.get('list', []).count(value),)
value = (kwargs.get('value') or [None])[0]
return ((kwargs.get('list') or []).count(value),)
class DataListEnumerate(ComfyNodeABC):
@@ -361,8 +361,8 @@ class DataListEnumerate(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def enumerate_list(self, **kwargs: list[Any]) -> tuple[list]:
input_list = kwargs.get('list', [])
start = kwargs.get('start', [0])[0]
input_list = kwargs.get('list') or []
start = (kwargs.get('start') or [0])[0]
return ([list(item) for item in enumerate(input_list, start=start)],)
@@ -392,7 +392,7 @@ class DataListExtend(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def extend(self, **kwargs: list[Any]) -> tuple[list[Any]]:
return (kwargs.get('list_a', []) + kwargs.get('list_b', []),)
return ((kwargs.get('list_a') or []) + (kwargs.get('list_b') or []),)
class DataListFilter(ComfyNodeABC):
@@ -425,8 +425,8 @@ class DataListFilter(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def filter_data(self, **kwargs: list[Any]) -> tuple[list[Any]]:
values = kwargs.get('value', [])
filters = kwargs.get('filter', [])
values = kwargs.get('value') or []
filters = kwargs.get('filter') or []
# Create a new list with only items where the filter is False
result = [_val for _val, _filter in zip(values, filters) if not _filter]
@@ -464,8 +464,8 @@ class DataListFilterSelect(ComfyNodeABC):
OUTPUT_IS_LIST = (True, True,)
def select(self, **kwargs: list[Any]) -> tuple[list[Any]]:
values = kwargs.get('value', [])
selects = kwargs.get('select', [])
values = kwargs.get('value') or []
selects = kwargs.get('select') or []
# Create a new list with only items where the filter is False
result_true, result_false = [], []
@@ -499,7 +499,7 @@ class DataListFirst(ComfyNodeABC):
INPUT_IS_LIST = True
def get_first_element(self, **kwargs: list[Any]) -> tuple[Any]:
input_list = kwargs.get('list', [])
input_list = kwargs.get('list') or []
return (input_list[0] if input_list else None,)
@@ -529,9 +529,9 @@ class DataListGetItem(ComfyNodeABC):
INPUT_IS_LIST = True
def get_item(self, **kwargs: list[Any]) -> tuple[Any]:
index = kwargs.get('index', [0])[0]
index = (kwargs.get('index') or [0])[0]
try:
return (kwargs.get('list', [])[index],)
return ((kwargs.get('list') or [])[index],)
except IndexError:
return (None,)
@@ -566,10 +566,10 @@ class DataListIndex(ComfyNodeABC):
INPUT_IS_LIST = True
def list_index(self, **kwargs: list[Any]) -> tuple[int]:
input_list = kwargs.get('list', [])
value = kwargs.get('value', [None])[0]
start = kwargs.get('start', [0])[0]
end = kwargs.get('end', [-1])[0]
input_list = kwargs.get('list') or []
value = (kwargs.get('value') or [None])[0]
start = (kwargs.get('start') or [0])[0]
end = (kwargs.get('end') or [-1])[0]
if end == -1:
end = len(input_list)
@@ -606,8 +606,8 @@ class DataListInsert(ComfyNodeABC):
OUTPUT_IS_LIST = (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])
result = (kwargs.get('list') or []).copy()
result.insert((kwargs.get('index') or [0])[0], (kwargs.get('item') or [None])[0])
return (result,)
@@ -635,7 +635,7 @@ class DataListLast(ComfyNodeABC):
INPUT_IS_LIST = True
def get_last_element(self, **kwargs: list[Any]) -> tuple[Any]:
input_list = kwargs.get('list', [])
input_list = kwargs.get('list') or []
return (input_list[-1] if input_list else None,)
@@ -662,7 +662,7 @@ class DataListLength(ComfyNodeABC):
INPUT_IS_LIST = True
def length(self, **kwargs: list[Any]) -> tuple[int]:
return (len(kwargs.get('list', [])),)
return (len(kwargs.get('list') or []),)
class DataListMax(ComfyNodeABC):
@@ -690,7 +690,7 @@ class DataListMax(ComfyNodeABC):
INPUT_IS_LIST = True
def find_max(self, **kwargs: list[Any]) -> tuple[Any]:
values = kwargs.get('list', [])
values = kwargs.get('list') or []
if not values:
return (None,)
@@ -727,7 +727,7 @@ class DataListMin(ComfyNodeABC):
INPUT_IS_LIST = True
def find_min(self, **kwargs: list[Any]) -> tuple[Any]:
values = kwargs.get('list', [])
values = kwargs.get('list') or []
if not values:
return (None,)
@@ -769,8 +769,8 @@ class DataListPop(ComfyNodeABC):
OUTPUT_IS_LIST = (True, False)
def pop(self, **kwargs: list[Any]) -> tuple[list[Any], Any]:
result = kwargs.get('list', []).copy()
index = kwargs.get('index', [-1])[0]
result = (kwargs.get('list') or []).copy()
index = (kwargs.get('index') or [-1])[0]
try:
item = result.pop(index)
return result, item
@@ -820,7 +820,7 @@ class DataListPopRandom(ComfyNodeABC):
def pop_random_element(self, **kwargs: list[Any]) -> tuple[list[Any], Any]:
import random
input_list = kwargs.get('list', []).copy()
input_list = (kwargs.get('list') or []).copy()
seed_values = kwargs.get('seed')
seed = seed_values[0] if seed_values is not None else None
rng = random.Random(seed) if seed is not None else random
@@ -889,8 +889,8 @@ class DataListRemove(ComfyNodeABC):
OUTPUT_IS_LIST = (True, False,)
def remove(self, **kwargs: list[Any]) -> tuple[list[Any], bool]:
result = kwargs.get('list', []).copy()
value = kwargs.get('value', [])
result = (kwargs.get('list') or []).copy()
value = kwargs.get('value') or []
try:
result.remove(value[0])
return result, True
@@ -922,7 +922,7 @@ class DataListReverse(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def reverse(self, **kwargs: list[Any]) -> tuple[list[Any]]:
result = kwargs.get('list', []).copy()
result = (kwargs.get('list') or []).copy()
result.reverse()
return (result,)
@@ -954,9 +954,9 @@ class DataListSetItem(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def set_item(self, **kwargs: list[Any]) -> tuple[Any]:
input_list = kwargs.get('list', [])
index = kwargs.get('index', [0])[0]
value = kwargs.get('value', [None])[0]
input_list = kwargs.get('list') or []
index = (kwargs.get('index') or [0])[0]
value = (kwargs.get('value') or [None])[0]
try:
result = input_list.copy()
result[index] = value
@@ -991,8 +991,8 @@ class DataListShuffle(ComfyNodeABC):
def shuffle_list(self, **kwargs: list[Any]) -> tuple[list[Any]]:
import random
input_list = kwargs.get('list', [])
seed = kwargs.get('seed', [0])[0]
input_list = kwargs.get('list') or []
seed = (kwargs.get('seed') or [0])[0]
random.seed(seed)
result = input_list.copy()
random.shuffle(result)
@@ -1029,10 +1029,10 @@ class DataListSlice(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def slice(self, **kwargs: list[Any]) -> tuple[list[Any]]:
input_list = kwargs.get('list', [])
start = kwargs.get('start', [0])[0]
stop = kwargs.get('stop', [INT_MAX])[0]
step = kwargs.get('step', [1])[0]
input_list = kwargs.get('list') or []
start = (kwargs.get('start') or [0])[0]
stop = (kwargs.get('stop') or [INT_MAX])[0]
step = (kwargs.get('step') or [1])[0]
return (input_list[start:stop:step],)
@@ -1066,9 +1066,9 @@ class DataListSort(ComfyNodeABC):
def sort(self, **kwargs: list[Any]) -> tuple[list[Any]]:
# Convert string to boolean
reverse = kwargs.get('reverse', ["False"])[0] == "True"
reverse = (kwargs.get('reverse') or ["False"])[0] == "True"
result = sorted(kwargs.get('list', []), reverse=reverse)
result = sorted(kwargs.get('list') or [], reverse=reverse)
return (result,)
@@ -1098,8 +1098,8 @@ class DataListSum(ComfyNodeABC):
INPUT_IS_LIST = True
def sum_list(self, **kwargs: list[Any]) -> tuple[int, float]:
input_list = kwargs.get('list', [])
start = kwargs.get('start', [0])[0]
input_list = kwargs.get('list') or []
start = (kwargs.get('start') or [0])[0]
result = sum(input_list, start)
return int(result), float(result)
@@ -1135,13 +1135,13 @@ class DataListZip(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def zip_lists(self, **kwargs: list[Any]) -> tuple[list[Any]]:
lists = [kwargs.get('list1', []), kwargs.get('list2', [])]
lists = [(kwargs.get('list1') or []), (kwargs.get('list2') or [])]
if 'list3' in kwargs:
lists.append(kwargs['list3'])
lists.append(kwargs['list3'] or [])
if 'list4' in kwargs:
lists.append(kwargs['list4'])
lists.append(kwargs['list4'] or [])
# Zip the lists together and convert each tuple to a list
result = [list(item) for item in zip(*lists)]
@@ -1172,7 +1172,7 @@ class DataListToList(ComfyNodeABC):
INPUT_IS_LIST = True
def convert(self, **kwargs: list[Any]) -> tuple[list[Any]]:
return (list(kwargs.get('list', [])).copy(),)
return (list(kwargs.get('list') or []).copy(),)
class DataListToSet(ComfyNodeABC):
@@ -1199,7 +1199,7 @@ class DataListToSet(ComfyNodeABC):
INPUT_IS_LIST = True
def convert(self, **kwargs: list[Any]) -> tuple[set[Any]]:
return (set(kwargs.get('list', [])),)
return (set(kwargs.get('list') or []),)
NODE_CLASS_MAPPINGS = {
+32 -1
View File
@@ -246,7 +246,7 @@ class DictCreateFromItemsDataList(ComfyNodeABC):
def create_from_items(self, **kwargs: list[Any]) -> tuple[dict]:
try:
# Check if items are valid (key-value pairs)
items = kwargs.get('item', [])
items = kwargs.get('item') or []
for item in items:
if not isinstance(item, tuple) and len(item) != 2:
raise ValueError("Each item must be a (key, value) pair")
@@ -280,6 +280,7 @@ class DictCreateFromItemsList(ComfyNodeABC):
def create_from_items(self, items: list) -> tuple[dict]:
try:
# Check if items are valid (key-value pairs)
items = items or []
for item in items:
if not isinstance(item, tuple) and len(item) != 2:
raise ValueError("Each item must be a (key, value) pair")
@@ -315,6 +316,8 @@ class DictCreateFromLists(ComfyNodeABC):
def create_from_lists(self, keys: List[KT], values: List[VT]) -> tuple[Dict[KT, VT]]:
# Pair keys with values up to the length of the shorter list
keys = keys or []
values = values or []
result = dict(zip(keys, values))
return (result,)
@@ -344,6 +347,9 @@ class DictCompare(ComfyNodeABC):
FUNCTION = "compare"
def compare(self, dict1: dict, dict2: dict) -> tuple[bool, list, list, list]:
dict1 = dict1 or {}
dict2 = dict2 or {}
are_equal = (dict1 == dict2)
keys1 = set(dict1.keys())
@@ -384,6 +390,7 @@ class DictContainsKey(ComfyNodeABC):
FUNCTION = "contains_key"
def contains_key(self, input_dict: dict, key: str) -> tuple[bool]:
input_dict = input_dict or {}
return (key in input_dict,)
@@ -411,6 +418,9 @@ class DictExcludeKeys(ComfyNodeABC):
FUNCTION = "exclude_keys"
def exclude_keys(self, input_dict: Mapping[KT, VT], keys_to_exclude: list) -> tuple[Mapping[KT, VT]]:
input_dict = input_dict if input_dict is not None else {}
keys_to_exclude = keys_to_exclude or []
if not(input_dict and keys_to_exclude):
return (input_dict,)
@@ -462,6 +472,9 @@ class DictFilterByKeys(ComfyNodeABC):
FUNCTION = "filter_by_keys"
def filter_by_keys(self, input_dict: Mapping[KT, VT], keys: list) -> tuple[Mapping[KT, VT]]:
input_dict = input_dict if input_dict is not None else {}
keys = keys or []
if not input_dict:
return (input_dict,)
if not keys:
@@ -518,6 +531,7 @@ class DictFromKeys(ComfyNodeABC):
FUNCTION = "from_keys"
def from_keys(self, keys: List[KT], value: VT = None) -> tuple[Dict[KT, VT]]:
keys = keys or []
return (dict.fromkeys(keys, value),)
@@ -549,6 +563,7 @@ class DictGet(ComfyNodeABC):
FUNCTION = "get"
def get(self, input_dict: Mapping[Any, VT], key: Any, default: T = None) -> tuple[_U[VT, T]]:
input_dict = input_dict if input_dict is not None else {}
return (input_dict.get(key, default),)
@@ -575,6 +590,7 @@ class DictGetKeysValues(ComfyNodeABC):
FUNCTION = "get_keys_values"
def get_keys_values(self, input_dict: Mapping[KT, VT]) -> tuple[List[KT], List[VT]]:
input_dict = input_dict if input_dict is not None else {}
keys = list(input_dict.keys())
values = list(input_dict.values())
return keys, values
@@ -611,6 +627,8 @@ class DictGetMultiple(ComfyNodeABC):
self,
input_dict: Mapping[Any, VT], keys: list, default: T = None
) -> tuple[List[_U[VT, T]]]:
input_dict = input_dict if input_dict is not None else {}
keys = keys or []
values = [input_dict.get(key, default) for key in keys]
return (values,)
@@ -639,6 +657,7 @@ class DictInvert(ComfyNodeABC):
FUNCTION = "invert"
def invert(self, input_dict: Mapping[KT, VT]) -> tuple[Mapping[VT, KT], bool]:
input_dict = input_dict if input_dict is not None else {}
try:
inverted = {v: k for k, v in input_dict.items()}
except Exception:
@@ -672,6 +691,7 @@ class DictItems(ComfyNodeABC):
FUNCTION = "items"
def items(self, input_dict: Mapping[KT, VT]) -> tuple[List[Tuple[KT, VT]]]:
input_dict = input_dict if input_dict is not None else {}
return (list(input_dict.items()),)
@@ -697,6 +717,7 @@ class DictKeys(ComfyNodeABC):
FUNCTION = "keys"
def keys(self, input_dict: Mapping[KT, Any]) -> tuple[List[KT]]:
input_dict = input_dict if input_dict is not None else {}
return (list(input_dict.keys()),)
@@ -722,6 +743,7 @@ class DictLength(ComfyNodeABC):
FUNCTION = "length"
def length(self, input_dict: Mapping) -> tuple[int]:
input_dict = input_dict if input_dict is not None else {}
return (len(input_dict),)
@@ -759,6 +781,7 @@ class DictMerge(ComfyNodeABC):
dict3: Mapping[KT3, VT3] = None,
dict4: Mapping[KT4, VT4] = None,
) -> tuple[Mapping[_U[KT, KT2, KT3, KT4], _U[VT, VT2, VT3, VT4]]]:
dict1 = dict1 if dict1 is not None else {}
extra_dicts = [x for x in (dict2, dict3, dict4) if x is not None]
if not extra_dicts:
return (dict1,)
@@ -803,6 +826,7 @@ class DictPop(ComfyNodeABC):
FUNCTION = "pop"
def pop(self, input_dict: Mapping[KT, VT], key: Any, default_value: T = None) -> tuple[Mapping[KT, VT], _U[VT, T]]:
input_dict = input_dict if input_dict is not None else {}
if key not in input_dict:
return input_dict, default_value
@@ -844,6 +868,7 @@ class DictPopItem(ComfyNodeABC):
FUNCTION = "popitem"
def popitem(self, input_dict: Mapping[KT, VT]) -> tuple[Mapping[KT, VT], _U[KT, str], _O[VT], bool]:
input_dict = input_dict if input_dict is not None else {}
if not input_dict:
return input_dict, "", None, False
@@ -893,6 +918,7 @@ class DictPopRandom(ComfyNodeABC):
return seed
def pop_random(self, input_dict: Mapping[KT, VT], seed: _O[int] = None) -> tuple[Mapping[KT, VT], _U[KT, str], _O[VT], bool]:
input_dict = input_dict if input_dict is not None else {}
if not input_dict:
return input_dict, "", None, False
@@ -933,6 +959,7 @@ class DictRemove(ComfyNodeABC):
FUNCTION = "remove"
def remove(self, input_dict: Mapping[KT, VT], key: Any) -> tuple[Mapping[KT, VT], bool]:
input_dict = input_dict if input_dict is not None else {}
if key not in input_dict:
return input_dict, False
@@ -968,6 +995,7 @@ class DictSet(ComfyNodeABC):
FUNCTION = "set"
def set(self, input_dict: Mapping[KT, VT], key: KT2, value: VT2) -> tuple[Mapping[_U[KT, KT2], _U[VT, VT2]]]:
input_dict = input_dict if input_dict is not None else {}
result = dict(input_dict)
result[key] = value
@@ -1001,6 +1029,7 @@ class DictSetDefault(ComfyNodeABC):
FUNCTION = "setdefault"
def setdefault(self, input_dict: Mapping[KT, VT], key: KT2, default_value: VT2 = None) -> tuple[Mapping[_U[KT, KT2], _U[VT, VT2]], _U[VT, VT2]]:
input_dict = input_dict if input_dict is not None else {}
result = dict(input_dict)
value = result.setdefault(key, default_value)
@@ -1033,6 +1062,7 @@ class DictUpdate(ComfyNodeABC):
FUNCTION = "update"
def update(self, dict1: Mapping[KT, VT], dict2: Mapping[KT2, VT2]) -> tuple[Mapping[_U[KT, KT2], _U[VT, VT2]]]:
dict1 = dict1 if dict1 is not None else {}
if not dict2:
return (dict1,)
@@ -1065,6 +1095,7 @@ class DictValues(ComfyNodeABC):
FUNCTION = "values"
def values(self, input_dict: Mapping[Any, VT]) -> tuple[List[VT]]:
input_dict = input_dict if input_dict is not None else {}
return (list(input_dict.values()),)
+23
View File
@@ -176,6 +176,7 @@ class ListAll:
FUNCTION = "check_all"
def check_all(self, list: list[Any]) -> tuple[bool]:
list = list if list is not None else []
return (all(list),)
@@ -201,6 +202,7 @@ class ListAny:
FUNCTION = "check_any"
def check_any(self, list: list[Any]) -> tuple[bool]:
list = list if list is not None else []
return (any(list),)
@@ -228,6 +230,7 @@ class ListAppend(ComfyNodeABC):
FUNCTION = "append"
def append(self, list: list[Any], item: Any) -> tuple[list[Any]]:
list = list if list is not None else []
result = list.copy()
result.append(item)
return (result,)
@@ -257,6 +260,7 @@ class ListContains(ComfyNodeABC):
FUNCTION = "contains"
def contains(self, list: list[Any], value: Any) -> tuple[bool]:
list = list if list is not None else []
return (value in list,)
@@ -284,6 +288,7 @@ class ListCount(ComfyNodeABC):
FUNCTION = "count"
def count(self, list: list[Any], value: Any) -> tuple[int]:
list = list if list is not None else []
return (list.count(value),)
@@ -312,6 +317,7 @@ class ListEnumerate:
FUNCTION = "enumerate_list"
def enumerate_list(self, list: list[Any], start: int = 0) -> tuple[list]:
list = list if list is not None else []
return ([__builtins__['list'](enumerate(list, start=start))],)
@@ -339,6 +345,8 @@ class ListExtend(ComfyNodeABC):
FUNCTION = "extend"
def extend(self, list1: list[Any], list2: list[Any]) -> tuple[list[Any]]:
list1 = list1 if list1 is not None else []
list2 = list2 if list2 is not None else []
result = list1.copy()
result.extend(list2)
return (result,)
@@ -395,6 +403,7 @@ class ListGetItem(ComfyNodeABC):
FUNCTION = "get_item"
def get_item(self, list: list[Any], index: int) -> tuple[Any]:
list = list if list is not None else []
try:
return (list[index],)
except IndexError:
@@ -430,6 +439,7 @@ class ListIndex(ComfyNodeABC):
FUNCTION = "index"
def index(self, list: list[Any], value: Any, start: int = 0, end: int = -1) -> tuple[int]:
list = list if list is not None else []
if end == -1:
end = len(list)
@@ -464,6 +474,7 @@ class ListInsert(ComfyNodeABC):
FUNCTION = "insert"
def insert(self, list: list[Any], index: int, item: Any) -> tuple[list[Any]]:
list = list if list is not None else []
result = list.copy()
result.insert(index, item)
return (result,)
@@ -517,6 +528,7 @@ class ListLength(ComfyNodeABC):
FUNCTION = "length"
def length(self, list: list[Any]) -> tuple[int]:
list = list if list is not None else []
return (len(list),)
@@ -614,6 +626,7 @@ class ListPop(ComfyNodeABC):
FUNCTION = "pop"
def pop(self, list: list[Any], index: int = -1) -> tuple[list[Any], Any]:
list = list if list is not None else []
result = list.copy()
try:
item = result.pop(index)
@@ -657,6 +670,7 @@ class ListPopRandom(ComfyNodeABC):
def pop_random_element(self, list: list[Any], seed=None) -> tuple[list[Any], Any]:
import random
list = list if list is not None else []
rng = random.Random(seed) if seed is not None else random
result = list.copy()
if result:
@@ -723,6 +737,7 @@ class ListRemove(ComfyNodeABC):
FUNCTION = "remove"
def remove(self, list: list[Any], value: Any) -> tuple[list[Any], bool]:
list = list if list is not None else []
result = list.copy()
try:
result.remove(value)
@@ -753,6 +768,7 @@ class ListReverse(ComfyNodeABC):
FUNCTION = "reverse"
def reverse(self, list: list[Any]) -> tuple[list[Any]]:
list = list if list is not None else []
result = list.copy()
result.reverse()
return (result,)
@@ -783,6 +799,7 @@ class ListSetItem(ComfyNodeABC):
FUNCTION = "set_item"
def set_item(self, list: list[Any], index: int, value: Any) -> tuple[list[Any]]:
list = list if list is not None else []
result = list.copy()
try:
result[index] = value
@@ -815,6 +832,7 @@ class ListShuffle(ComfyNodeABC):
def shuffle_list(self, list: list[Any], seed: int) -> tuple[list[Any]]:
import random
list = list if list is not None else []
random.seed(seed)
result = list.copy()
random.shuffle(result)
@@ -850,6 +868,7 @@ class ListSlice(ComfyNodeABC):
def slice(self, list: list[Any], start: int = 0, stop: int = INT_MAX,
step: int = 1) -> tuple[list[Any]]:
list = list if list is not None else []
return (list[start:stop:step],)
class ListSort(ComfyNodeABC):
@@ -880,6 +899,7 @@ class ListSort(ComfyNodeABC):
def sort(self, list: list[Any], reverse: str = "False") -> tuple[list[Any]]:
# Convert string to boolean
reverse_bool = (reverse == "True")
list = list if list is not None else []
# Use sorted to create a new sorted list
try:
@@ -915,6 +935,7 @@ class ListSum:
FUNCTION = "sum_list"
def sum_list(self, list: list[Any], start: int = 0) -> tuple[int, float]:
list = list if list is not None else []
result = sum(list, start)
return result, float(result)
@@ -944,6 +965,7 @@ class ListToDataList(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def convert(self, list) -> tuple[list[Any]]:
list = list if list is not None else []
return (list,)
@@ -970,6 +992,7 @@ class ListToSet(ComfyNodeABC):
FUNCTION = "convert"
def convert(self, list: list[Any]) -> tuple[set[Any]]:
list = list if list is not None else []
return (set(list),)
+32
View File
@@ -16,6 +16,11 @@ except:
from ._dynamic_input import ContainsDynamicDict
def _empty_set() -> set:
"""Return an empty set, independent of a node parameter named ``set``."""
return set()
class SetCreate(ComfyNodeABC):
"""
Creates a new SET from items.
@@ -175,6 +180,7 @@ class SetAdd(ComfyNodeABC):
FUNCTION = "add"
def add(self, set: set[Any], item: Any) -> tuple[set[Any]]:
set = set if set is not None else _empty_set()
result = set.copy()
result.add(item)
return (result,)
@@ -203,6 +209,7 @@ class SetAll(ComfyNodeABC):
FUNCTION = "check_all"
def check_all(self, set: set[Any]) -> tuple[bool]:
set = set if set is not None else _empty_set()
return (all(set),)
@@ -229,6 +236,7 @@ class SetAny(ComfyNodeABC):
FUNCTION = "check_any"
def check_any(self, set: set[Any]) -> tuple[bool]:
set = set if set is not None else _empty_set()
return (any(set),)
@@ -256,6 +264,7 @@ class SetContains(ComfyNodeABC):
FUNCTION = "contains"
def contains(self, set: set[Any], value: Any) -> tuple[bool]:
set = set if set is not None else _empty_set()
return (value in set,)
@@ -283,6 +292,8 @@ class SetDifference(ComfyNodeABC):
FUNCTION = "difference"
def difference(self, set1: set[Any], set2: set[Any]) -> tuple[set[Any]]:
set1 = set1 if set1 is not None else set()
set2 = set2 if set2 is not None else set()
result = set1.copy()
result.difference_update(set2)
return (result,)
@@ -312,6 +323,7 @@ class SetDiscard(ComfyNodeABC):
FUNCTION = "discard"
def discard(self, set: set[Any], item: Any) -> tuple[set[Any]]:
set = set if set is not None else _empty_set()
result = set.copy()
result.discard(item)
return (result,)
@@ -347,6 +359,7 @@ class SetEnumerate(ComfyNodeABC):
FUNCTION = "enumerate_set"
def enumerate_set(self, set: set[Any], start: int = 0) -> tuple[list]:
set = set if set is not None else _empty_set()
return (list(enumerate(set, start=start)),)
@@ -378,6 +391,8 @@ class SetIntersection(ComfyNodeABC):
FUNCTION = "intersection"
def intersection(self, set1: set[Any], set2: set[Any], set3=None, set4=None) -> tuple[set[Any]]:
set1 = set1 if set1 is not None else set()
set2 = set2 if set2 is not None else set()
result = set1.copy()
result.intersection_update(set2)
@@ -413,6 +428,8 @@ class SetIsDisjoint(ComfyNodeABC):
FUNCTION = "is_disjoint"
def is_disjoint(self, set1: set[Any], set2: set[Any]) -> tuple[bool]:
set1 = set1 if set1 is not None else set()
set2 = set2 if set2 is not None else set()
return (set1.isdisjoint(set2),)
@@ -440,6 +457,8 @@ class SetIsSubset(ComfyNodeABC):
FUNCTION = "is_subset"
def is_subset(self, set1: set[Any], set2: set[Any]) -> tuple[bool]:
set1 = set1 if set1 is not None else set()
set2 = set2 if set2 is not None else set()
return (set1.issubset(set2),)
@@ -467,6 +486,8 @@ class SetIsSuperset(ComfyNodeABC):
FUNCTION = "is_superset"
def is_superset(self, set1: set[Any], set2: set[Any]) -> tuple[bool]:
set1 = set1 if set1 is not None else set()
set2 = set2 if set2 is not None else set()
return (set1.issuperset(set2),)
@@ -492,6 +513,7 @@ class SetLength(ComfyNodeABC):
FUNCTION = "length"
def length(self, set: set[Any]) -> tuple[int]:
set = set if set is not None else _empty_set()
return (len(set),)
@@ -519,6 +541,7 @@ class SetPop(ComfyNodeABC):
FUNCTION = "pop"
def pop(self, set: set[Any]) -> tuple[set[Any], Any]:
set = set if set is not None else _empty_set()
result = set.copy()
try:
item = result.pop()
@@ -562,6 +585,7 @@ class SetPopRandom(ComfyNodeABC):
def pop_random_element(self, set: set[Any], seed=None) -> tuple[set[Any], Any]:
import random
set = set if set is not None else _empty_set()
rng = random.Random(seed) if seed is not None else random
result = set.copy()
if result:
@@ -596,6 +620,7 @@ class SetRemove(ComfyNodeABC):
FUNCTION = "remove"
def remove(self, set: set[Any], item: Any) -> tuple[set[Any], bool]:
set = set if set is not None else _empty_set()
result = set.copy()
try:
result.remove(item)
@@ -634,6 +659,7 @@ class SetSum(ComfyNodeABC):
FUNCTION = "sum_set"
def sum_set(self, set: set[Any], start: int = 0) -> tuple[int, float]:
set = set if set is not None else _empty_set()
result = sum(set, start)
return result, float(result)
@@ -662,6 +688,8 @@ class SetSymmetricDifference(ComfyNodeABC):
FUNCTION = "symmetric_difference"
def symmetric_difference(self, set1: set[Any], set2: set[Any]) -> tuple[set[Any]]:
set1 = set1 if set1 is not None else set()
set2 = set2 if set2 is not None else set()
result = set1.copy()
result.symmetric_difference_update(set2)
return (result,)
@@ -695,6 +723,8 @@ class SetUnion(ComfyNodeABC):
FUNCTION = "union"
def union(self, set1: set[Any], set2: set[Any], set3=None, set4=None) -> tuple[set[Any]]:
set1 = set1 if set1 is not None else set()
set2 = set2 if set2 is not None else set()
result = set1.copy()
result.update(set2)
@@ -732,6 +762,7 @@ class SetToDataList(ComfyNodeABC):
OUTPUT_IS_LIST = (True,)
def convert(self, set) -> tuple[list[Any]]:
set = set if set is not None else _empty_set()
return (list(set),)
@@ -759,6 +790,7 @@ class SetToList(ComfyNodeABC):
FUNCTION = "convert"
def convert(self, set: set[Any]) -> tuple[list[Any]]:
set = set if set is not None else _empty_set()
return (list(set),)
+3
View File
@@ -371,6 +371,7 @@ class StringFormatMap(ComfyNodeABC):
FUNCTION = "format_map"
def format_map(self, template, mapping):
mapping = mapping if mapping is not None else {}
try:
result = template.format_map(mapping)
return (result,)
@@ -778,6 +779,7 @@ class StringDataListJoin(ComfyNodeABC):
def join(self, sep, strings):
separator = sep[0] # everything comes as a list, so sep is list[str]
strings = strings if strings is not None else []
return (separator.join(strings),)
@@ -806,6 +808,7 @@ class StringListJoin(ComfyNodeABC):
FUNCTION = "join"
def join(self, sep, strings):
strings = strings if strings is not None else []
return (sep.join(strings),)