Add BOOLEAN, FLOAT, INT and data lists.

Bump to version 0.0.3
This commit is contained in:
StableLlama
2025-05-07 18:13:42 +02:00
parent da171fb171
commit 2a0c87f9a4
10 changed files with 1436 additions and 157 deletions
-4
View File
@@ -6,10 +6,6 @@ __all__ = [
"WEB_DIRECTORY",
]
__author__ = """Basic data handling"""
__email__ = "stablellama@tutanota.com"
__version__ = "0.0.1"
from .src.basic_data_handling import NODE_CLASS_MAPPINGS
from .src.basic_data_handling import NODE_DISPLAY_NAME_MAPPINGS
+4 -6
View File
@@ -4,18 +4,16 @@ build-backend = "setuptools.build_meta"
[project]
name = "basic_data_handling"
version = "0.0.2"
description = """Basic Python functions for manipulating data that every programmer
is used to. Currently supported: STRING."""
version = "0.0.3"
description = """Basic Python functions for manipulating data that every programmer is used to.
Currently supported: BOOLEAN, FLOAT, INT, STRING and data lists."""
authors = [
{name = "Basic data handling"}
]
readme = "README.md"
license = {text = "MIT license"}
classifiers = []
dependencies = [
]
dependencies = []
[project.optional-dependencies]
dev = [
View File
+11 -5
View File
@@ -1,11 +1,17 @@
from . import string_nodes, list_nodes, dict_nodes
from . import boolean_nodes, dict_nodes, float_nodes, int_nodes, list_nodes, string_nodes
NODE_CLASS_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(string_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(list_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(boolean_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(dict_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(float_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(int_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(list_nodes.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(string_nodes.NODE_CLASS_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS.update(string_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(list_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(boolean_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(dict_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(float_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(int_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(list_nodes.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(string_nodes.NODE_DISPLAY_NAME_MAPPINGS)
+162
View File
@@ -0,0 +1,162 @@
from inspect import cleandoc
class BooleanAnd:
"""
Returns the logical AND result of two boolean values.
This node takes two boolean inputs and returns their logical AND result.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False}),
"input2": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "and_operation"
def and_operation(self, input1: bool, input2: bool) -> tuple[bool]:
return (input1 and input2,)
class BooleanOr:
"""
Returns the logical OR result of two boolean values.
This node takes two boolean inputs and returns their logical OR result.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False}),
"input2": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "or_operation"
def or_operation(self, input1: bool, input2: bool) -> tuple[bool]:
return (input1 or input2,)
class BooleanNot:
"""
Returns the logical NOT result of a boolean value.
This node takes one boolean input and returns its logical NOT result.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "not_operation"
def not_operation(self, input: bool) -> tuple[bool]:
return (not input,)
class BooleanXor:
"""
Returns the logical XOR result of two boolean values.
This node takes two boolean inputs and returns their logical XOR result.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False}),
"input2": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "xor_operation"
def xor_operation(self, input1: bool, input2: bool) -> tuple[bool]:
return (input1 != input2,)
class BooleanNand:
"""
Returns the logical NAND result of two boolean values.
This node takes two boolean inputs and returns their logical NAND result.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False}),
"input2": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "nand_operation"
def nand_operation(self, input1: bool, input2: bool) -> tuple[bool]:
return (not (input1 and input2),)
class BooleanNor:
"""
Returns the logical NOR result of two boolean values.
This node takes two boolean inputs and returns their logical NOR result.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input1": ("BOOLEAN", {"default": False}),
"input2": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/BOOLEAN"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "nor_operation"
def nor_operation(self, input1: bool, input2: bool) -> tuple[bool]:
return (not (input1 or input2),)
NODE_CLASS_MAPPINGS = {
"BooleanAnd": BooleanAnd,
"BooleanOr": BooleanOr,
"BooleanNot": BooleanNot,
"BooleanXor": BooleanXor,
"BooleanNand": BooleanNand,
"BooleanNor": BooleanNor,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"BooleanAnd": "and",
"BooleanOr": "or",
"BooleanNot": "not",
"BooleanXor": "xor",
"BooleanNand": "nand",
"BooleanNor": "nor",
}
+274
View File
@@ -0,0 +1,274 @@
from inspect import cleandoc
class FloatAdd:
"""
Adds two floating-point numbers.
This node takes two floats as input and returns their sum.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float1": ("FLOAT", {"default": 0.0}),
"float2": ("FLOAT", {"default": 0.0}),
}
}
RETURN_TYPES = ("FLOAT",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "add"
def add(self, float1: float, float2: float) -> tuple[float]:
return (float1 + float2,)
class FloatSubtract:
"""
Subtracts one floating-point number from another.
This node takes two floats as input and returns the result of subtracting
the second float from the first.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float1": ("FLOAT", {"default": 0.0}),
"float2": ("FLOAT", {"default": 0.0}),
}
}
RETURN_TYPES = ("FLOAT",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "subtract"
def subtract(self, float1: float, float2: float) -> tuple[float]:
return (float1 - float2,)
class FloatMultiply:
"""
Multiplies two floating-point numbers.
This node takes two floats as input and returns their product.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float1": ("FLOAT", {"default": 1.0}),
"float2": ("FLOAT", {"default": 1.0}),
}
}
RETURN_TYPES = ("FLOAT",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "multiply"
def multiply(self, float1: float, float2: float) -> tuple[float]:
return (float1 * float2,)
class FloatDivide:
"""
Divides one floating-point number by another.
This node takes two floats as input and returns the result of division.
It raises a ValueError if the divisor is 0.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float1": ("FLOAT", {"default": 1.0}),
"float2": ("FLOAT", {"default": 1.0}),
}
}
RETURN_TYPES = ("FLOAT",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "divide"
def divide(self, float1: float, float2: float) -> tuple[float]:
if float2 == 0.0:
raise ValueError("Cannot divide by zero.")
return (float1 / float2,)
class FloatPower:
"""
Raises one floating-point number to the power of another.
This node takes two floats as input and returns the result of raising
the first float to the power of the second.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"base": ("FLOAT", {"default": 1.0}),
"exponent": ("FLOAT", {"default": 1.0}),
}
}
RETURN_TYPES = ("FLOAT",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "power"
def power(self, base: float, exponent: float) -> tuple[float]:
return (base ** exponent,)
class FloatRound:
"""
Rounds a floating-point number to the specified number of decimal places.
This node takes a float and an integer for decimal places as inputs,
and returns the rounded result.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float_value": ("FLOAT", {"default": 0.0}),
"decimal_places": ("INT", {"default": 2, "min": 0}),
}
}
RETURN_TYPES = ("FLOAT",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "round"
def round(self, float_value: float, decimal_places: int) -> tuple[float]:
return (round(float_value, decimal_places),)
class FloatIsInteger:
"""
Checks if a floating-point number is an integer.
This node takes a floating-point number as input and returns True if the number
is an integer, otherwise False.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float_value": ("FLOAT", {"default": 0.0}),
}
}
RETURN_TYPES = ("BOOLEAN",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "is_integer"
def is_integer(self, float_value: float) -> tuple[bool]:
return (float_value.is_integer(),)
class FloatAsIntegerRatio:
"""
Returns the integer ratio of a floating-point number.
This node takes a floating-point number and returns two integers,
which represent the ratio as numerator and denominator.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float_value": ("FLOAT", {"default": 0.0}),
}
}
RETURN_TYPES = ("INT", "INT")
RETURN_NAMES = ("numerator", "denominator")
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "as_integer_ratio"
def as_integer_ratio(self, float_value: float) -> tuple[int, int]:
# Decompose the float into numerator and denominator
numerator, denominator = float_value.as_integer_ratio()
return numerator, denominator
class FloatHex:
"""
Converts a floating-point number to its hexadecimal representation.
This node takes a float as input and returns its hexadecimal string representation.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"float_value": ("FLOAT", {"default": 0.0}),
}
}
RETURN_TYPES = ("STRING",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "to_hex"
def to_hex(self, float_value: float) -> tuple[str]:
return (float_value.hex(),)
class FloatFromHex:
"""
Converts a hexadecimal string to its corresponding floating-point number.
This node takes a hexadecimal float string as input and returns the float.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"hex_value": ("STRING", {"default": "0x0.0p+0"}),
}
}
RETURN_TYPES = ("FLOAT",)
CATEGORY = "Basic/FLOAT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "from_hex"
def from_hex(self, hex_value: str) -> tuple[float]:
return (float.fromhex(hex_value),)
NODE_CLASS_MAPPINGS = {
"FloatAdd": FloatAdd,
"FloatSubtract": FloatSubtract,
"FloatMultiply": FloatMultiply,
"FloatDivide": FloatDivide,
"FloatPower": FloatPower,
"FloatRound": FloatRound,
"FloatIsInteger": FloatIsInteger,
"FloatAsIntegerRatio": FloatAsIntegerRatio,
"FloatHex": FloatHex,
"FloatFromHex": FloatFromHex,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"FloatAdd": "add",
"FloatSubtract": "subtract",
"FloatMultiply": "multiply",
"FloatDivide": "divide",
"FloatPower": "power",
"FloatRound": "round",
"FloatIsInteger": "is_integer",
"FloatAsIntegerRatio": "as_integer_ratio",
"FloatHex": "to_hex",
"FloatFromHex": "from_hex",
}
+285
View File
@@ -0,0 +1,285 @@
from inspect import cleandoc
from typing import Literal
class IntAdd:
"""
Adds two integers.
This node takes two integers as input and returns their sum.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int1": ("INT", {"default": 0}),
"int2": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Basic/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "add"
def add(self, int1: int, int2: int) -> tuple[int]:
return (int1 + int2,)
class IntSubtract:
"""
Subtracts one integer from another.
This node takes two integers as input and returns the result of subtracting
the second integer from the first.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int1": ("INT", {"default": 0}),
"int2": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Basic/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "subtract"
def subtract(self, int1: int, int2: int) -> tuple[int]:
return (int1 - int2,)
class IntMultiply:
"""
Multiplies two integers.
This node takes two integers as input and returns their product.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int1": ("INT", {"default": 1}),
"int2": ("INT", {"default": 1}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Basic/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "multiply"
def multiply(self, int1: int, int2: int) -> tuple[int]:
return (int1 * int2,)
class IntDivide:
"""
Divides one integer by another.
This node takes two integers as input and returns the result of integer
division. It raises a ValueError if the divisor is 0.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int1": ("INT", {"default": 1}),
"int2": ("INT", {"default": 1}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Basic/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "divide"
def divide(self, int1: int, int2: int) -> tuple[int]:
if int2 == 0:
raise ValueError("Cannot divide by zero.")
return (int1 // int2,)
class IntModulus:
"""
Returns the modulus of two integers.
This node takes two integers as input and returns the remainder when the
first integer is divided by the second.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int1": ("INT", {"default": 0}),
"int2": ("INT", {"default": 1}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Basic/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "modulus"
def modulus(self, int1: int, int2: int) -> tuple[int]:
if int2 == 0:
raise ValueError("Cannot perform modulus operation by zero.")
return (int1 % int2,)
class IntPower:
"""
Raises one integer to the power of another.
This node takes two integers as input and returns the result of raising
the first integer to the power of the second.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"base": ("INT", {"default": 1}),
"exponent": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Basic/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "power"
def power(self, base: int, exponent: int) -> tuple[int]:
return (base**exponent,)
class IntBitLength:
"""
Returns the number of bits required to represent an integer in binary.
This node takes an integer as input and returns the number of bits needed
to represent it, excluding the sign and leading zeros.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int_value": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Additional Methods/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "bit_length"
def bit_length(self, int_value: int) -> tuple[int]:
return (int_value.bit_length(),)
class IntToBytes:
"""
Converts an integer to its byte representation.
This node takes an integer, byte length, and byte order as inputs and
returns the bytes object representation of the integer.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int_value": ("INT", {"default": 0}),
"length": ("INT", {"default": 4, "min": 1}),
"byteorder": (["big", "little"], {"default": "big"}),
"signed": (["True", "False"], {"default": "False"}),
}
}
RETURN_TYPES = ("BYTES",)
CATEGORY = "Additional Methods/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "to_bytes"
def to_bytes(self, int_value: int, length: int, byteorder: Literal["big"|"little"], signed: Literal["True"|"False"]) -> tuple[int]:
signed_bool = (signed == "True")
return (int_value.to_bytes(length, byteorder=byteorder, signed=signed_bool),)
class IntFromBytes:
"""
Converts a bytes object to an integer.
This class method takes bytes, byte order, and signed flag as inputs and
returns an integer.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"bytes_value": ("BYTES", {}),
"byteorder": (["big", "little"], {"default": "big"}),
"signed": (["True", "False"], {"default": "False"}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Additional Methods/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "from_bytes"
def from_bytes(self, bytes_value, byteorder: Literal["big"|"little"], signed: Literal["True"|"False"]) -> tuple[int]:
signed_bool = (signed == "True")
return (int.from_bytes(bytes_value, byteorder=byteorder, signed=signed_bool),)
class IntBitCount:
"""
Returns the number of 1 bits in the binary representation of an integer.
This node takes an integer as input and returns the count of set bits.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"int_value": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Additional Methods/INT"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "bit_count"
def bit_count(self, int_value: int) -> tuple[int]:
# Introduced in Python 3.10
return (int_value.bit_count(),)
NODE_CLASS_MAPPINGS = {
"IntAdd": IntAdd,
"IntSubtract": IntSubtract,
"IntMultiply": IntMultiply,
"IntDivide": IntDivide,
"IntModulus": IntModulus,
"IntPower": IntPower,
"IntBitLength": IntBitLength,
"IntToBytes": IntToBytes,
"IntFromBytes": IntFromBytes,
"IntBitCount": IntBitCount,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"IntAdd": "add",
"IntSubtract": "subtract",
"IntMultiply": "multiply",
"IntDivide": "divide",
"IntModulus": "modulus",
"IntPower": "power",
"IntBitLength": "bit_length",
"IntToBytes": "to_bytes",
"IntFromBytes": "from_bytes",
"IntBitCount": "bit_count",
}
+645 -12
View File
@@ -1,3 +1,5 @@
from typing import Any
from inspect import cleandoc
class ListAppend:
@@ -10,20 +12,389 @@ class ListAppend:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_list": ("LIST",),
"item": ("ANY",),
"optional": {
"list":("*",{}),
"item":("*",{}),
}
}
RETURN_TYPES = ("LIST",)
CATEGORY = "Basic/LIST"
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "append"
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: dict[str, list]) -> tuple[list[Any]]:
result = kwargs.get('list', []).copy()
item = kwargs.get('item', [])
if len(item) > 0:
result.append(item[0])
return (result,)
class ListExtend:
"""
Extends a list by appending elements from another list.
This node takes two lists as input and returns a new list that contains
all elements from both lists.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"optional": {
"list_a": ("*",{}),
"list_b": ("*",{}),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "extend"
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: dict[str, list]) -> tuple[list[Any]]:
return (kwargs.get('list_a', []) + kwargs.get('list_b', []),)
class ListInsert:
"""
Inserts an item at a specified position in a list.
This node takes a list, an index, and any item as inputs, then returns a new
list with the item inserted at the specified index.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"index": ("INT", {"default": 0}),
"item": ("*",),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "insert"
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: dict[str, list]) -> tuple[list[Any]]:
result = kwargs.get('list', []).copy()
result.insert(kwargs.get('index', [0])[0], kwargs.get('item', [None])[0])
return (result,)
class ListRemove:
"""
Removes the first occurrence of a specified value from a list.
This node takes a list and a value as inputs, then returns a new list with
the first occurrence of the value removed. Raises a ValueError if the value is not present.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"value": ("*",),
}
}
RETURN_TYPES = ("*", "BOOLEAN",)
RETURN_NAMES = ("list", "success",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "remove"
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: dict[str, list]) -> tuple[list[Any], bool]:
result = kwargs.get('list', []).copy()
value = kwargs.get('value', [])
try:
result.remove(value[0])
return result, True
except ValueError:
return result, False
class ListPop:
"""
Removes and returns an item at a specified position in a list.
This node takes a list and an index as inputs, then returns both the new list
with the item removed and the removed item. If no index is specified,
removes and returns the last item.
When the list is empty, the item is None.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
},
"optional": {
"index": ("INT", {"default": -1}),
}
}
RETURN_TYPES = ("*", "*")
RETURN_NAMES = ("list", "item")
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "pop"
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True, False)
@classmethod
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool:
if input_types["index"] not in ("INT"):
return "index must be an INT type"
return True
def pop(self, **kwargs: dict[str, list]) -> tuple[list[Any], Any]:
result = kwargs.get('list', []).copy()
index = kwargs.get('index', [-1])[0]
try:
item = result.pop(index)
return result, item
except IndexError:
return result, None
class ListClear:
"""
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/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: dict[str, list]) -> tuple[[]]:
# Return a new empty list rather than modifying the input
return ([],)
class ListIndex:
"""
Returns the index of the first occurrence of a value in a list.
This node takes a list and a value as inputs, then returns the index of the first
occurrence of the value. Optional start and end parameters limit the search to a slice
of the list. Returns -1 if the value is not present.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"value": ("*",),
},
"optional": {
"start": ("INT", {"default": 0}),
"end": ("INT", {"default": -1}),
}
}
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("index",)
CATEGORY = "Basic/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: dict[str, list]) -> 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]
if end == -1:
end = len(input_list)
try:
return (input_list.index(value, start, end),)
except ValueError:
return (-1,)
class ListCount:
"""
Counts the number of occurrences of a value in a list.
This node takes a list and a value as inputs, then returns the number of times
the value appears in the list.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"value": ("*",),
}
}
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("count",)
CATEGORY = "Basic/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: dict[str, list]) -> tuple[int]:
value = kwargs.get('value', [None])[0]
return (kwargs.get('list', []).count(value),)
class ListSort:
"""
Sorts the items in a list.
This node takes a list as input and returns a new sorted list.
Options include sorting in reverse order and using a key function.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
},
"optional": {
"reverse": (["False", "True"], {"default": "False"}),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "sort"
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: dict[str, list]) -> tuple[list[Any]]:
# Convert string to boolean
reverse = kwargs.get('reverse', ["False"])[0] == "True"
result = sorted(kwargs.get('list', []), reverse=reverse)
return (result,)
class ListReverse:
"""
Reverses the order of items in a list.
This node takes a list as input and returns a new list with the items in reversed order.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "reverse"
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: dict[str, list]) -> tuple[list[Any]]:
result = kwargs.get('list', []).copy()
result.reverse()
return (result,)
class ListCopy:
"""
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/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: dict[str, list]) -> tuple[list[Any]]:
return (kwargs.get('list', []).copy(),)
def append(self, input_list, item):
input_list.append(item)
return (input_list,)
class ListLength:
"""
@@ -35,24 +406,286 @@ class ListLength:
def INPUT_TYPES(cls):
return {
"required": {
"input_list": ("LIST",),
"list": ("*",),
}
}
RETURN_TYPES = ("INT",)
CATEGORY = "Basic/LIST"
RETURN_NAMES = ("length",)
CATEGORY = "Basic/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: dict[str, list]) -> tuple[int]:
return (len(kwargs.get('list', [])),)
class ListSlice:
"""
Creates a slice of a list.
This node takes a list and start/stop/step parameters, and returns a new list
containing the specified slice of the original list.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
},
"optional": {
"start": ("INT", {"default": 0}),
"stop": ("INT", {"default": -1}),
"step": ("INT", {"default": 1}),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "slice"
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: dict[str, list]) -> tuple[list[Any]]:
input_list = kwargs.get('list', [])
start = kwargs.get('start', [0])[0]
stop = kwargs.get('stop', [-1])[0]
step = kwargs.get('step', [1])[0]
if stop == -1:
stop = len(input_list)
return (input_list[start:stop:step],)
class ListGetItem:
"""
Retrieves an item at a specified position in a list.
This node takes a list and an index as inputs, then returns the item at the specified index.
Negative indices count from the end of the list.
Out of range indices return None.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_list": ("*",),
"index": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("item",)
CATEGORY = "Basic/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: dict[str, list]) -> tuple[Any]:
index = kwargs.get('index', [0])[0]
try:
return (kwargs.get('list', [])[index],)
except IndexError:
return (None,)
class ListSetItem:
"""
Sets an item at a specified position in a list.
This node takes a list, an index, and a value, then returns a new list with
the item at the specified index replaced by the value.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list": ("*",),
"index": ("INT", {"default": 0}),
"value": ("*",),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "set_item"
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: dict[str, list]) -> tuple[Any]:
input_list = kwargs.get('list', [])
index = kwargs.get('index', [0])[0]
value = kwargs.get('value', [None])[0]
try:
result = input_list.copy()
result[index] = value
return (result,)
except IndexError:
raise IndexError(f"Index {index} out of range for list of length {len(input_list)}")
class ListContains:
"""
Checks if a list contains a specified value.
This node takes a list and a value as inputs, then returns True if the value
is present in the list, and False otherwise.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_list": ("*",),
"value": ("*",),
}
}
RETURN_TYPES = ("BOOLEAN",)
RETURN_NAMES = ("contains",)
CATEGORY = "Basic/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: dict[str, list]) -> tuple[bool]:
value = kwargs.get('value', [])
if len(value) == 0:
return (False,)
return (value[0] in kwargs.get('list', []),)
class ListCreateEmpty:
"""
Creates a new empty list.
This node creates and returns an empty list.
"""
@classmethod
def INPUT_TYPES(cls):
return {"required": {}}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "create_empty"
OUTPUT_IS_LIST = (True,)
def create_empty(self) -> tuple[[]]:
return ([],)
class ListZip:
"""
Combines multiple lists element-wise.
This node takes multiple data lists as input and returns a new data list
where each item is a list containing the corresponding elements from the input lists.
The length of the output list will be equal to the length of the shortest input list.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"list1": ("*",),
"list2": ("*",),
},
"optional": {
"list3": ("*",),
"list4": ("*",),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("list",)
CATEGORY = "Basic/list"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "zip_lists"
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: dict[str, list]) -> tuple[list[Any]]:
lists = [kwargs.get('list1', []), kwargs.get('list2', [])]
if 'list3' in kwargs:
lists.append(kwargs['list3'])
if 'list4' in kwargs:
lists.append(kwargs['list4'])
# Zip the lists together and convert each tuple to a list
result = [list(item) for item in zip(*lists)]
return (result,)
def length(self, input_list):
return (len(input_list),)
NODE_CLASS_MAPPINGS = {
"ListAppend": ListAppend,
"ListExtend": ListExtend,
"ListInsert": ListInsert,
"ListRemove": ListRemove,
"ListPop": ListPop,
"ListClear": ListClear,
"ListIndex": ListIndex,
"ListCount": ListCount,
"ListSort": ListSort,
"ListReverse": ListReverse,
"ListCopy": ListCopy,
"ListLength": ListLength,
"ListSlice": ListSlice,
"ListGetItem": ListGetItem,
"ListSetItem": ListSetItem,
"ListContains": ListContains,
"ListCreateEmpty": ListCreateEmpty,
"ListZip": ListZip,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ListAppend": "append",
"ListExtend": "extend",
"ListInsert": "insert",
"ListRemove": "remove",
"ListPop": "pop",
"ListClear": "clear",
"ListIndex": "index",
"ListCount": "count",
"ListSort": "sort",
"ListReverse": "reverse",
"ListCopy": "copy",
"ListLength": "length",
"ListSlice": "slice",
"ListGetItem": "get_item",
"ListSetItem": "set_item",
"ListContains": "contains",
"ListCreateEmpty": "create_empty",
"ListZip": "zip",
}
-118
View File
@@ -1,118 +0,0 @@
from inspect import cleandoc
class Example:
"""
A example node
Class methods
-------------
INPUT_TYPES (dict):
Tell the main program input parameters of nodes.
IS_CHANGED:
optional method to control when the node is re executed.
Attributes
----------
RETURN_TYPES (`tuple`):
The type of each element in the output tulple.
RETURN_NAMES (`tuple`):
Optional: The name of each output in the output tulple.
FUNCTION (`str`):
The name of the entry-point method. For example, if `FUNCTION = "execute"` then it will run Example().execute()
OUTPUT_NODE ([`bool`]):
If this node is an output node that outputs a result/image from the graph. The SaveImage node is an example.
The backend iterates on these output nodes and tries to execute all their parents if their parent graph is properly connected.
Assumed to be False if not present.
CATEGORY (`str`):
The category the node should appear in the UI.
execute(s) -> tuple || None:
The entry point method. The name of this method must be the same as the value of property `FUNCTION`.
For example, if `FUNCTION = "execute"` then this method's name must be `execute`, if `FUNCTION = "foo"` then it must be `foo`.
"""
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
"""
Return a dictionary which contains config for all input fields.
Some types (string): "MODEL", "VAE", "CLIP", "CONDITIONING", "LATENT", "IMAGE", "INT", "STRING", "FLOAT".
Input types "INT", "STRING" or "FLOAT" are special values for fields on the node.
The type can be a list for selection.
Returns: `dict`:
- Key input_fields_group (`string`): Can be either required, hidden or optional. A node class must have property `required`
- Value input_fields (`dict`): Contains input fields config:
* Key field_name (`string`): Name of a entry-point method's argument
* Value field_config (`tuple`):
+ First value is a string indicate the type of field or a list for selection.
+ Secound value is a config for type "INT", "STRING" or "FLOAT".
"""
return {
"required": {
"image": ("Image", { "tooltip": "This is an image"}),
"int_field": ("INT", {
"default": 0,
"min": 0, #Minimum value
"max": 4096, #Maximum value
"step": 64, #Slider's step
"display": "number" # Cosmetic only: display as "number" or "slider"
}),
"float_field": ("FLOAT", {
"default": 1.0,
"min": 0.0,
"max": 10.0,
"step": 0.01,
"round": 0.001, #The value represeting the precision to round to, will be set to the step value by default. Can be set to False to disable rounding.
"display": "number"}),
"print_to_screen": (["enable", "disable"],),
"string_field": ("STRING", {
"multiline": False, #True if you want the field to look like the one on the ClipTextEncode node
"default": "Hello World!"
}),
},
}
RETURN_TYPES = ("IMAGE",)
#RETURN_NAMES = ("image_output_name",)
DESCRIPTION = cleandoc(__doc__)
FUNCTION = "test"
#OUTPUT_NODE = False
#OUTPUT_TOOLTIPS = ("",) # Tooltips for the output node
CATEGORY = "Example"
def test(self, image, string_field, int_field, float_field, print_to_screen):
if print_to_screen == "enable":
print(f"""Your input contains:
string_field aka input text: {string_field}
int_field: {int_field}
float_field: {float_field}
""")
#do some processing on the image, in this example I just invert it
image = 1.0 - image
return (image,)
"""
The node will always be re executed if any of the inputs change but
this method can be used to force the node to execute again even when the inputs don't change.
You can make this node return a number or a string. This value will be compared to the one returned the last time the node was
executed, if it is different the node will be executed again.
This method is used in the core repo for the LoadImage node where they return the image hash as a string, if the image hash
changes between executions the LoadImage node is executed again.
"""
#@classmethod
#def IS_CHANGED(s, image, string_field, int_field, float_field, print_to_screen):
# return ""
# A dictionary that contains all nodes you want to export with their names
# NOTE: names should be globally unique
NODE_CLASS_MAPPINGS = {
"Example": Example
}
# A dictionary that contains the friendly/humanly readable titles for the nodes
NODE_DISPLAY_NAME_MAPPINGS = {
"Example": "Example Node"
}
+55 -12
View File
@@ -914,10 +914,11 @@ class StringRsplit:
}
}
RETURN_TYPES = ("LIST",)
RETURN_TYPES = ("STRING",)
CATEGORY = "Basic/STRING"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "rsplit"
OUTPUT_IS_LIST = (True,)
def rsplit(self, string, sep=None, maxsplit=-1):
if sep == "":
@@ -1205,10 +1206,11 @@ class StringRsplit:
}
}
RETURN_TYPES = ("LIST",)
RETURN_TYPES = ("STRING",)
CATEGORY = "Basic/STRING"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "rsplit"
OUTPUT_IS_LIST = (True,)
def rsplit(self, string, sep=None, maxsplit=-1):
if sep == "":
@@ -1216,6 +1218,7 @@ class StringRsplit:
return (string.rsplit(sep, maxsplit),)
class StringRstrip:
"""
Removes trailing characters from the string.
@@ -1246,6 +1249,34 @@ class StringRstrip:
return (string.rstrip(chars),)
class StringJoin:
"""
Joins strings from a data list with a specified separator.
This node takes a data list of strings and concatenates them, with the specified
separator string between each element. The separator is inserted between the strings,
not at the beginning or end of the result.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"sep": ("STRING", {"default": " ", "defaultInput": True}),
"strings": ("STRING", {"forceInput": True}),
}
}
RETURN_TYPES = ("STRING",)
CATEGORY = "Basic/STRING"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "join"
INPUT_IS_LIST = True # The "strings" input accepts a data list
def join(self, sep, strings):
separator = sep[0] # everything comes as a list, so sep is list[str]
return (separator.join(strings),)
class StringLjust:
"""
Left-aligns the string within a field of a given width.
@@ -1465,10 +1496,11 @@ class StringRsplit:
}
}
RETURN_TYPES = ("LIST",)
RETURN_TYPES = ("STRING",) # Changed from "LIST" to "STRING"
CATEGORY = "Basic/STRING"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "rsplit"
OUTPUT_IS_LIST = (True,) # This indicates that the output is a data list
def rsplit(self, string, sep=None, maxsplit=-1):
if sep == "":
@@ -1510,8 +1542,10 @@ class StringSplit:
"""
Splits the string at the specified separator.
This node returns a list of strings by splitting the input string at the specified separator.
If maxsplit is provided, at most maxsplit splits are done.
This node splits the input string at the specified separator and returns a
data list containing all parts. If maxsplit is provided, at most maxsplit
splits are done.
If the separator is not specified or is None, any whitespace string is a separator.
"""
@classmethod
@@ -1526,15 +1560,19 @@ class StringSplit:
}
}
RETURN_TYPES = ("LIST",)
RETURN_TYPES = ("STRING",)
FUNCTION = "split"
CATEGORY = "Basic/STRING"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "split"
OUTPUT_IS_LIST = (True,) # This indicates that the output is a data list
def split(self, string, sep=None, maxsplit=-1):
if sep == "":
sep = None
return (string.split(sep, maxsplit),)
parts = string.split(sep, maxsplit)
# Return a data list where each item is a separate string
return (parts,)
class StringSplitlines:
@@ -1551,17 +1589,20 @@ class StringSplitlines:
"string": ("STRING", {"default": "", "defaultInput": True}),
},
"optional": {
"keepends": ("BOOLEAN", {"default": False}),
"keepends": (["False", "True"], {"default": "False"}),
}
}
RETURN_TYPES = ("LIST",)
RETURN_TYPES = ("STRING",)
CATEGORY = "Basic/STRING"
DESCRIPTION = cleandoc(__doc__ or "")
FUNCTION = "splitlines"
OUTPUT_IS_LIST = (True,)
def splitlines(self, string, keepends=False):
return (string.splitlines(keepends),)
def splitlines(self, string, keepends="False"):
keepends_bool = (keepends == "True")
lines = string.splitlines(keepends_bool)
return (lines,)
class StringStartswith:
@@ -1750,6 +1791,7 @@ NODE_CLASS_MAPPINGS = {
"StringIsSpace": StringIsSpace,
"StringIsTitle": StringIsTitle,
"StringIsUpper": StringIsUpper,
"StringJoin": StringJoin,
"StringLjust": StringLjust,
"StringLower": StringLower,
"StringLstrip": StringLstrip,
@@ -1795,6 +1837,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"StringIsSpace": "isspace",
"StringIsTitle": "istitle",
"StringIsUpper": "isupper",
"StringJoin": "join",
"StringLjust": "ljust",
"StringLower": "lower",
"StringLstrip": "lstrip",