Version 0.4.1

Add all, any, enumerate and sum nodes for LIST, SET and data list
This commit is contained in:
StableLlama
2025-06-09 15:08:56 +02:00
parent 622f471bfa
commit e4d59c24a7
8 changed files with 657 additions and 19 deletions
+40
View File
@@ -274,6 +274,46 @@ ComfyUI has different data types that serve different purposes:
- Supports built-in ComfyUI iteration over each item
- Best for:
- Working directly with multiple items in parallel
- Processing each item in a collection separately
- When you need ComfyUI's automatic iteration functionality
### 2. LIST
- A Python list passed as a single ComfyUI variable
- Must be processed as a complete unit by compatible nodes
- Operations apply to the entire LIST at once
- Best for:
- Storing and manipulating structured data as a single unit
- When you need to preserve ordered collections
- Passing complex data structures between nodes
### 3. SET
- A Python set passed as a single ComfyUI variable
- Unordered collection of unique items
- Useful for membership testing, removing duplicates, and set operations
- Best for:
- When you need to ensure uniqueness of items
- Performing mathematical set operations (union, intersection, difference)
- Efficient membership testing (contains operation)
- When item order doesn't matter
## Control Flow Nodes
Control flow nodes provide mechanisms to direct the flow of execution in your ComfyUI workflows, allowing for conditional processing and dynamic execution paths.
### Available Control Flow Nodes:
#### Conditional Processing
- **if/else** - Routes execution based on a boolean condition
- **if/elif/.../else** - Supports multiple conditional branches
- **switch/case** - Selects from multiple options based on an index
#### Execution Management
- **disable flow** - Conditionally enables or disables a flow
- **flow select** - Directs output to either "true" or "false" path
- **force calculation** - Prevents caching and forces recalculation
- **force execution order** - Controls the sequence of node execution
These control flow nodes enable building more complex, dynamic workflows with decision-making capabilities based on runtime conditions.
- Batch processing scenarios
- When you need to apply the same operation to multiple inputs
- When your operation needs to work with individual items separately
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "basic_data_handling"
version = "0.4.0"
version = "0.4.1"
description = """Basic Python functions for manipulating data that every programmer is used to.
Comprehensive node collection for data manipulation in ComfyUI workflows.
+120
View File
@@ -153,6 +153,56 @@ class DataListCreateFromString(ComfyNodeABC):
return (values[:-1],)
class DataListAll(ComfyNodeABC):
"""
Check if all elements in the data list are true.
Returns true if all elements are true (or if the list is empty).
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": (IO.ANY, {}),
}
}
RETURN_TYPES = (IO.BOOLEAN,)
RETURN_NAMES = ("result",)
CATEGORY = "Basic/Data List"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_all"
INPUT_IS_LIST = True
def check_all(self, **kwargs: list[Any]) -> tuple[bool]:
return (all(kwargs.get('list', [])),)
class DataListAny(ComfyNodeABC):
"""
Check if any element in the data list is true.
Returns true if at least one element is true. Returns false if the list is empty.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": (IO.ANY, {}),
}
}
RETURN_TYPES = (IO.BOOLEAN,)
RETURN_NAMES = ("result",)
CATEGORY = "Basic/Data List"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_any"
INPUT_IS_LIST = True
def check_any(self, **kwargs: list[Any]) -> tuple[bool]:
return (any(kwargs.get('list', [])),)
class DataListAppend(ComfyNodeABC):
"""
Adds an item to the end of a list.
@@ -243,6 +293,37 @@ class DataListCount(ComfyNodeABC):
return (kwargs.get('list', []).count(value),)
class DataListEnumerate(ComfyNodeABC):
"""
Enumerate a data list, returning a list of [index, value] pairs.
Optionally, specify a starting value for the index.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": (IO.ANY, {}),
},
"optional": {
"start": (IO.INT, {"default": 0}),
}
}
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/Data List"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "enumerate_list"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True,)
def enumerate_list(self, **kwargs: list[Any]) -> tuple[list]:
input_list = kwargs.get('list', [])
start = kwargs.get('start', [0])[0]
return ([list(item) for item in enumerate(input_list, start=start)],)
class DataListExtend(ComfyNodeABC):
"""
Extends a list by appending elements from another list.
@@ -876,6 +957,37 @@ class DataListSort(ComfyNodeABC):
return (result,)
class DataListSum(ComfyNodeABC):
"""
Sum all elements of the data list.
Returns 0 for an empty list.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": (IO.NUMBER, {}),
},
"optional": {
"start": (IO.INT, {"default": 0}),
}
}
RETURN_TYPES = (IO.INT, IO.FLOAT,)
RETURN_NAMES = ("int_sum", "float_sum",)
CATEGORY = "Basic/Data List"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "sum_list"
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]
result = sum(input_list, start)
return int(result), float(result)
class DataListZip(ComfyNodeABC):
"""
Combines multiple lists element-wise.
@@ -975,9 +1087,12 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: DataListCreateFromFloat": DataListCreateFromFloat,
"Basic data handling: DataListCreateFromInt": DataListCreateFromInt,
"Basic data handling: DataListCreateFromString": DataListCreateFromString,
"Basic data handling: DataListAll": DataListAll,
"Basic data handling: DataListAny": DataListAny,
"Basic data handling: DataListAppend": DataListAppend,
"Basic data handling: DataListContains": DataListContains,
"Basic data handling: DataListCount": DataListCount,
"Basic data handling: DataListEnumerate": DataListEnumerate,
"Basic data handling: DataListExtend": DataListExtend,
"Basic data handling: DataListFilter": DataListFilter,
"Basic data handling: DataListFilterSelect": DataListFilterSelect,
@@ -997,6 +1112,7 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: DataListSetItem": DataListSetItem,
"Basic data handling: DataListSlice": DataListSlice,
"Basic data handling: DataListSort": DataListSort,
"Basic data handling: DataListSum": DataListSum,
"Basic data handling: DataListZip": DataListZip,
"Basic data handling: DataListToList": DataListToList,
"Basic data handling: DataListToSet": DataListToSet,
@@ -1008,9 +1124,12 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: DataListCreateFromFloat": "create Data List from FLOATs",
"Basic data handling: DataListCreateFromInt": "create Data List from INTs",
"Basic data handling: DataListCreateFromString": "create Data List from STRINGs",
"Basic data handling: DataListAll": "all",
"Basic data handling: DataListAny": "any",
"Basic data handling: DataListAppend": "append",
"Basic data handling: DataListContains": "contains",
"Basic data handling: DataListCount": "count",
"Basic data handling: DataListEnumerate": "enumerate",
"Basic data handling: DataListExtend": "extend",
"Basic data handling: DataListFilter": "filter",
"Basic data handling: DataListFilterSelect": "filter select",
@@ -1030,6 +1149,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: DataListSetItem": "set item",
"Basic data handling: DataListSlice": "slice",
"Basic data handling: DataListSort": "sort",
"Basic data handling: DataListSum": "sum",
"Basic data handling: DataListZip": "zip",
"Basic data handling: DataListToList": "convert to LIST",
"Basic data handling: DataListToSet": "convert to SET",
+108 -2
View File
@@ -143,6 +143,52 @@ class ListCreateFromString(ComfyNodeABC):
return (values,)
class ListAll:
"""
Check if all elements in the list are true.
Returns true if all elements are true (or if the list is empty).
"""
@classmethod
def INPUT_TYPES(cls) -> dict:
return {
"required": {
"list": ("LIST",),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/LIST"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_all"
def check_all(self, list: list[Any]) -> tuple[bool]:
return (all(list),)
class ListAny:
"""
Check if any element in the list is true.
Returns true if at least one element is true. Returns false if the list is empty.
"""
@classmethod
def INPUT_TYPES(cls) -> dict:
return {
"required": {
"list": ("LIST",),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/LIST"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_any"
def check_any(self, list: list[Any]) -> tuple[bool]:
return (any(list),)
class ListAppend(ComfyNodeABC):
"""
Adds an item to the end of a LIST.
@@ -222,6 +268,32 @@ class ListCount(ComfyNodeABC):
return (list.count(value),)
class ListEnumerate:
"""
Enumerate a list, returning a list of [index, value] pairs.
Optionally, specify a starting value for the index.
"""
@classmethod
def INPUT_TYPES(cls) -> dict:
return {
"required": {
"list": ("LIST",),
},
"optional": {
"start": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("LIST",)
CATEGORY = "Basic/LIST"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "enumerate_list"
def enumerate_list(self, list: list[Any], start: int = 0) -> tuple[list]:
return ([__builtins__['list'](enumerate(list, start=start))],)
class ListExtend(ComfyNodeABC):
"""
Extends a LIST by appending elements from another LIST.
@@ -665,7 +737,6 @@ class ListSetItem(ComfyNodeABC):
except IndexError:
raise IndexError(f"Index {index} out of range for LIST of length {len(list)}")
class ListSlice(ComfyNodeABC):
"""
Creates a slice of a LIST.
@@ -695,7 +766,6 @@ class ListSlice(ComfyNodeABC):
step: int = 1) -> tuple[list[Any]]:
return (list[start:stop:step],)
class ListSort(ComfyNodeABC):
"""
Sorts the items in a LIST.
@@ -732,6 +802,33 @@ class ListSort(ComfyNodeABC):
return (list.copy(),)
class ListSum:
"""
Sum all elements of the list.
Returns 0 for an empty list.
"""
@classmethod
def INPUT_TYPES(cls) -> dict:
return {
"required": {
"list": ("LIST",),
},
"optional": {
"start": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("INT", "FLOAT",)
CATEGORY = "Basic/LIST"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "sum_list"
def sum_list(self, list: list[Any], start: int = 0) -> tuple[int, float]:
result = sum(list, start)
return result, float(result)
class ListToDataList(ComfyNodeABC):
"""
Converts a LIST object into a ComfyUI data list.
@@ -757,6 +854,7 @@ class ListToDataList(ComfyNodeABC):
def convert(self, list) -> tuple[list[Any]]:
return (list,)
class ListToSet(ComfyNodeABC):
"""
Converts a LIST into a SET.
@@ -787,9 +885,12 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: ListCreateFromFloat": ListCreateFromFloat,
"Basic data handling: ListCreateFromInt": ListCreateFromInt,
"Basic data handling: ListCreateFromString": ListCreateFromString,
"Basic data handling: ListAll": ListAll,
"Basic data handling: ListAny": ListAny,
"Basic data handling: ListAppend": ListAppend,
"Basic data handling: ListContains": ListContains,
"Basic data handling: ListCount": ListCount,
"Basic data handling: ListEnumerate": ListEnumerate,
"Basic data handling: ListExtend": ListExtend,
"Basic data handling: ListFirst": ListFirst,
"Basic data handling: ListGetItem": ListGetItem,
@@ -807,6 +908,7 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: ListSetItem": ListSetItem,
"Basic data handling: ListSlice": ListSlice,
"Basic data handling: ListSort": ListSort,
"Basic data handling: ListSum": ListSum,
"Basic data handling: ListToDataList": ListToDataList,
"Basic data handling: ListToSet": ListToSet,
}
@@ -817,9 +919,12 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: ListCreateFromFloat": "create LIST from FLOATs",
"Basic data handling: ListCreateFromInt": "create LIST from INTs",
"Basic data handling: ListCreateFromString": "create LIST from STRINGs",
"Basic data handling: ListAll": "all",
"Basic data handling: ListAny": "any",
"Basic data handling: ListAppend": "append",
"Basic data handling: ListContains": "contains",
"Basic data handling: ListCount": "count",
"Basic data handling: ListEnumerate": "enumerate",
"Basic data handling: ListExtend": "extend",
"Basic data handling: ListFirst": "first",
"Basic data handling: ListGetItem": "get item",
@@ -837,6 +942,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: ListSetItem": "set item",
"Basic data handling: ListSlice": "slice",
"Basic data handling: ListSort": "sort",
"Basic data handling: ListSum": "sum",
"Basic data handling: ListToDataList": "convert to Data List",
"Basic data handling: ListToSet": "convert to SET",
}
+122
View File
@@ -168,6 +168,56 @@ class SetAdd(ComfyNodeABC):
return (result,)
class SetAll(ComfyNodeABC):
"""
Checks if all elements in the SET are true.
This node takes a SET as input and returns True if all elements in the SET
evaluate to True (or if the SET is empty), and False otherwise.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"set": ("SET", {}),
}
}
RETURN_TYPES = (IO.BOOLEAN,)
RETURN_NAMES = ("all_true",)
CATEGORY = "Basic/SET"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_all"
def check_all(self, set: set[Any]) -> tuple[bool]:
return (all(set),)
class SetAny(ComfyNodeABC):
"""
Checks if any element in the SET is true.
This node takes a SET as input and returns True if at least one element
in the SET evaluates to True, and False otherwise (including if the SET is empty).
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"set": ("SET", {}),
}
}
RETURN_TYPES = (IO.BOOLEAN,)
RETURN_NAMES = ("any_true",)
CATEGORY = "Basic/SET"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "check_any"
def check_any(self, set: set[Any]) -> tuple[bool]:
return (any(set),)
class SetContains(ComfyNodeABC):
"""
Checks if a SET contains a specified value.
@@ -248,6 +298,37 @@ class SetDiscard(ComfyNodeABC):
return (result,)
class SetEnumerate(ComfyNodeABC):
"""
Enumerates elements in a SET.
This node takes a SET as input and returns a LIST of tuples where each tuple
contains an index and a value from the SET. The start parameter specifies the
initial index value (default is 0).
Note: Since SETs are unordered, the enumeration order is arbitrary but consistent
within a single operation.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"set": ("SET", {}),
},
"optional": {
"start": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("LIST",)
CATEGORY = "Basic/SET"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "enumerate_set"
def enumerate_set(self, set: set[Any], start: int = 0) -> tuple[list]:
return (list(enumerate(set, start=start)),)
class SetIntersection(ComfyNodeABC):
"""
Returns the intersection of two or more SETs.
@@ -481,6 +562,39 @@ class SetRemove(ComfyNodeABC):
return result, False
class SetSum(ComfyNodeABC):
"""
Calculates the sum of all elements in a SET.
This node takes a SET as input and returns the sum of all its elements.
The optional start parameter specifies the initial value (default is 0).
Note: This operation requires all elements to be numeric or otherwise
compatible with addition. If the SET contains mixed or incompatible types,
it may raise an error.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"set": ("SET", {}),
},
"optional": {
"start": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("INT", "FLOAT",)
RETURN_NAMES = ("sum_int", "sum_float",)
CATEGORY = "Basic/SET"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "sum_set"
def sum_set(self, set: set[Any], start: int = 0) -> tuple[int, float]:
result = sum(set, start)
return result, float(result)
class SetSymmetricDifference(ComfyNodeABC):
"""
Returns the symmetric difference between two SETs.
@@ -604,9 +718,12 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: SetCreateFromInt": SetCreateFromInt,
"Basic data handling: SetCreateFromString": SetCreateFromString,
"Basic data handling: SetAdd": SetAdd,
"Basic data handling: SetAll": SetAll,
"Basic data handling: SetAny": SetAny,
"Basic data handling: SetContains": SetContains,
"Basic data handling: SetDifference": SetDifference,
"Basic data handling: SetDiscard": SetDiscard,
"Basic data handling: SetEnumerate": SetEnumerate,
"Basic data handling: SetIntersection": SetIntersection,
"Basic data handling: SetIsDisjoint": SetIsDisjoint,
"Basic data handling: SetIsSubset": SetIsSubset,
@@ -615,6 +732,7 @@ NODE_CLASS_MAPPINGS = {
"Basic data handling: SetPop": SetPop,
"Basic data handling: SetPopRandom": SetPopRandom,
"Basic data handling: SetRemove": SetRemove,
"Basic data handling: SetSum": SetSum,
"Basic data handling: SetSymmetricDifference": SetSymmetricDifference,
"Basic data handling: SetUnion": SetUnion,
"Basic data handling: SetToDataList": SetToDataList,
@@ -628,9 +746,12 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: SetCreateFromInt": "create SET from INTs",
"Basic data handling: SetCreateFromString": "create SET from STRINGs",
"Basic data handling: SetAdd": "add",
"Basic data handling: SetAll": "all",
"Basic data handling: SetAny": "any",
"Basic data handling: SetContains": "contains",
"Basic data handling: SetDifference": "difference",
"Basic data handling: SetDiscard": "discard",
"Basic data handling: SetEnumerate": "enumerate",
"Basic data handling: SetIntersection": "intersection",
"Basic data handling: SetIsDisjoint": "is disjoint",
"Basic data handling: SetIsSubset": "is subset",
@@ -639,6 +760,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Basic data handling: SetPop": "pop",
"Basic data handling: SetPopRandom": "pop random",
"Basic data handling: SetRemove": "remove",
"Basic data handling: SetSum": "sum",
"Basic data handling: SetSymmetricDifference": "symmetric difference",
"Basic data handling: SetUnion": "union",
"Basic data handling: SetToDataList": "convert to Data List",
+73 -16
View File
@@ -1,35 +1,39 @@
import pytest
from src.basic_data_handling.data_list_nodes import (
DataListAll,
DataListAny,
DataListAppend,
DataListContains,
DataListCount,
DataListCreate,
DataListCreateFromBoolean,
DataListCreateFromFloat,
DataListCreateFromInt,
DataListCreateFromString,
DataListEnumerate,
DataListExtend,
DataListInsert,
DataListRange,
DataListRemove,
DataListPop,
DataListPopRandom,
DataListIndex,
DataListCount,
DataListSort,
DataListReverse,
DataListLength,
DataListSlice,
DataListGetItem,
DataListSetItem,
DataListContains,
DataListZip,
DataListFilter,
DataListFilterSelect,
DataListFirst,
DataListGetItem,
DataListIndex,
DataListInsert,
DataListLast,
DataListMin,
DataListLength,
DataListMax,
DataListMin,
DataListPop,
DataListPopRandom,
DataListRange,
DataListRemove,
DataListReverse,
DataListSetItem,
DataListSlice,
DataListSort,
DataListSum,
DataListToList,
DataListToSet,
DataListZip,
)
@@ -405,6 +409,59 @@ def test_to_set():
assert node.convert(list=[1, "two", 3.0]) == ({1, "two", 3.0},)
def test_all():
node = DataListAll()
# All true values
assert node.check_all(list=[True, True, 1, "text"]) == (True,)
# Contains false value
assert node.check_all(list=[True, False, True]) == (False,)
# Empty list (returns True)
assert node.check_all(list=[]) == (True,)
def test_any():
node = DataListAny()
# Contains true value
assert node.check_any(list=[False, True, False]) == (True,)
# All false values
assert node.check_any(list=[False, 0, "", None]) == (False,)
# Empty list (returns False)
assert node.check_any(list=[]) == (False,)
def test_enumerate():
node = DataListEnumerate()
# Basic enumeration starting from 0
assert node.enumerate_list(list=['a', 'b', 'c']) == ([[0, 'a'], [1, 'b'], [2, 'c']],)
# Custom start index
assert node.enumerate_list(list=['x', 'y', 'z'], start=[10]) == ([[10, 'x'], [11, 'y'], [12, 'z']],)
# Empty list
assert node.enumerate_list(list=[]) == ([],)
def test_sum():
node = DataListSum()
# Integer sum
int_sum, float_sum = node.sum_list(list=[1, 2, 3])
assert int_sum == 6
assert float_sum == 6.0
# Mixed number types
int_sum, float_sum = node.sum_list(list=[1, 2.5, 3])
assert int_sum == 6 # Integer part of the sum
assert float_sum == 6.5 # Full float sum
# With start value
int_sum, float_sum = node.sum_list(list=[1, 2, 3], start=[10])
assert int_sum == 16
assert float_sum == 16.0
# Empty list with start value
int_sum, float_sum = node.sum_list(list=[], start=[5])
assert int_sum == 5
assert float_sum == 5.0
def test_range():
node = DataListRange()
# Test with default start (0) and step (1)
+97
View File
@@ -1,5 +1,7 @@
import pytest
from src.basic_data_handling.list_nodes import (
ListAll,
ListAny,
ListAppend,
ListContains,
ListCount,
@@ -8,6 +10,7 @@ from src.basic_data_handling.list_nodes import (
ListCreateFromFloat,
ListCreateFromInt,
ListCreateFromString,
ListEnumerate,
ListExtend,
ListFirst,
ListGetItem,
@@ -25,6 +28,7 @@ from src.basic_data_handling.list_nodes import (
ListSetItem,
ListSlice,
ListSort,
ListSum,
ListToDataList,
ListToSet,
)
@@ -247,3 +251,96 @@ def test_list_range():
# Test error case: step=0
with pytest.raises(ValueError, match="Step cannot be zero"):
node.create_range(start=0, stop=5, step=0)
def test_list_all():
node = ListAll()
# All true values
assert node.check_all([True, True, True]) == (True,)
# Mixed values (all truthy)
assert node.check_all([1, "text", [1, 2]]) == (True,)
# Contains a false value
assert node.check_all([True, False, True]) == (False,)
# Contains a falsy value
assert node.check_all([True, 0, True]) == (False,)
# Empty list (returns True according to Python's all() behavior)
assert node.check_all([]) == (True,)
def test_list_any():
node = ListAny()
# All true values
assert node.check_any([True, True, True]) == (True,)
# Mixed values with at least one true
assert node.check_any([False, True, False]) == (True,)
# Mixed values with at least one truthy
assert node.check_any([0, "", 1]) == (True,)
# All false values
assert node.check_any([False, False, False]) == (False,)
# All falsy values
assert node.check_any([0, "", []]) == (False,)
# Empty list (returns False according to Python's any() behavior)
assert node.check_any([]) == (False,)
def test_list_enumerate():
node = ListEnumerate()
# Basic enumeration with default start=0
result = node.enumerate_list(["a", "b", "c"])
assert result == ([[(0, "a"), (1, "b"), (2, "c")]],)
# Enumeration with custom start
result = node.enumerate_list(["x", "y", "z"], start=10)
assert result == ([[(10, "x"), (11, "y"), (12, "z")]],)
# Enumeration of empty list
result = node.enumerate_list([])
assert result == ([[]],)
# Enumeration of list with mixed types
result = node.enumerate_list([1, "text", True])
assert result == ([[(0, 1), (1, "text"), (2, True)]],)
def test_list_sum():
node = ListSum()
# Sum of integers
int_result, float_result = node.sum_list([1, 2, 3, 4])
assert int_result == 10
assert float_result == 10.0
# Sum with default start value (0)
int_result, float_result = node.sum_list([5, 10, 15])
assert int_result == 30
assert float_result == 30.0
# Sum with custom start value
int_result, float_result = node.sum_list([1, 2, 3], start=10)
assert int_result == 16
assert float_result == 16.0
# Sum of floats (should still return both int and float)
int_result, float_result = node.sum_list([1.5, 2.5, 3.0])
assert int_result == 7.0 # Note: This will be a float, but returned as first value
assert float_result == 7.0
# Sum of empty list
int_result, float_result = node.sum_list([])
assert int_result == 0
assert float_result == 0.0
# Sum of mixed numbers
int_result, float_result = node.sum_list([1, 2.5, 3])
assert int_result == 6.5
assert float_result == 6.5
+96
View File
@@ -1,6 +1,8 @@
#import pytest
from src.basic_data_handling.set_nodes import (
SetAdd,
SetAll,
SetAny,
SetContains,
SetCreate,
SetCreateFromBoolean,
@@ -9,6 +11,7 @@ from src.basic_data_handling.set_nodes import (
SetCreateFromString,
SetDifference,
SetDiscard,
SetEnumerate,
SetIntersection,
SetIsDisjoint,
SetIsSubset,
@@ -17,6 +20,7 @@ from src.basic_data_handling.set_nodes import (
SetPop,
SetPopRandom,
SetRemove,
SetSum,
SetSymmetricDifference,
SetToDataList,
SetToList,
@@ -246,3 +250,95 @@ def test_set_to_data_list():
# Mixed types
result = node.convert({1, "string", True})
assert set(result[0]) == {1, "string", True} # Can't check order, just content
def test_set_all():
node = SetAll()
# Test with all truthy values
assert node.check_all({1, True, "string", 3.14}) == (True,)
# Test with one falsy value
assert node.check_all({1, False, "string"}) == (False,)
# Test with all falsy values
assert node.check_all({False, 0, "", None}) == (False,)
# Test with empty set (should return True per Python's all() behavior)
assert node.check_all(set()) == (True,)
def test_set_any():
node = SetAny()
# Test with all truthy values
assert node.check_any({1, True, "string", 3.14}) == (True,)
# Test with one truthy value
assert node.check_any({0, False, "", 1}) == (True,)
# Test with all falsy values
assert node.check_any({False, 0, "", None}) == (False,)
# Test with empty set (should return False per Python's any() behavior)
assert node.check_any(set()) == (False,)
def test_set_enumerate():
node = SetEnumerate()
# Basic test with default start=0
result = node.enumerate_set({10, 20, 30})
assert isinstance(result, tuple)
assert isinstance(result[0], list)
# Convert to set of tuples for comparison (order may vary)
result_set = {tuple(item) for item in result[0]}
assert result_set == {(0, 10), (1, 20), (2, 30)}
# Test with custom start value
result = node.enumerate_set({10, 20, 30}, start=5)
result_set = {tuple(item) for item in result[0]}
assert result_set == {(5, 10), (6, 20), (7, 30)}
# Test with empty set
result = node.enumerate_set(set())
assert result[0] == []
# Test with mixed types
result = node.enumerate_set({1, "string", False})
assert len(result[0]) == 3
# Check format but not exact values due to arbitrary order
for item in result[0]:
assert isinstance(item, tuple)
assert len(item) == 2
assert isinstance(item[0], int)
def test_set_sum():
node = SetSum()
# Test with integer set
int_result, float_result = node.sum_set({1, 2, 3})
assert int_result == 6
assert float_result == 6.0
# Test with float set
int_result, float_result = node.sum_set({1.5, 2.5, 3.0})
assert int_result == 7.0
assert float_result == 7.0
# Test with mixed numeric types
int_result, float_result = node.sum_set({1, 2.5, 3})
assert int_result == 6.5
assert float_result == 6.5
# Test with custom start value
int_result, float_result = node.sum_set({1, 2, 3}, start=10)
assert int_result == 16
assert float_result == 16.0
# Test with empty set
int_result, float_result = node.sum_set(set(), start=5)
assert int_result == 5
assert float_result == 5.0