Add STRING escape and unescape nodes.
Fix test cases for containers and add those for the new string nodes
This commit is contained in:
@@ -811,7 +811,6 @@ class DataListSlice(ComfyNodeABC):
|
||||
start = kwargs.get('start', [0])[0]
|
||||
stop = kwargs.get('stop', [INT_MAX])[0]
|
||||
step = kwargs.get('step', [1])[0]
|
||||
print(f"start: {start}, stop: {stop}, step: {step}; input_list: {input_list}")
|
||||
|
||||
return (input_list[start:stop:step],)
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ class ListCreateFromFloat(ComfyNodeABC):
|
||||
FUNCTION = "create_list"
|
||||
|
||||
def create_list(self, **kwargs: list[Any]) -> tuple[list[Any]]:
|
||||
values = [float(value) for value in kwargs.values()]
|
||||
return (values[:-1],)
|
||||
values = [float(value) for value in list(kwargs.values())[:-1]]
|
||||
return (values,)
|
||||
|
||||
|
||||
class ListCreateFromInt(ComfyNodeABC):
|
||||
@@ -117,8 +117,8 @@ class ListCreateFromInt(ComfyNodeABC):
|
||||
FUNCTION = "create_list"
|
||||
|
||||
def create_list(self, **kwargs: list[Any]) -> tuple[list[Any]]:
|
||||
values = [int(value) for value in kwargs.values()]
|
||||
return (values[:-1],)
|
||||
values = [int(value) for value in list(kwargs.values())[:-1]]
|
||||
return (values,)
|
||||
|
||||
|
||||
class ListCreateFromString(ComfyNodeABC):
|
||||
@@ -143,8 +143,8 @@ class ListCreateFromString(ComfyNodeABC):
|
||||
FUNCTION = "create_list"
|
||||
|
||||
def create_list(self, **kwargs: list[Any]) -> tuple[list[Any]]:
|
||||
values = [str(value) for value in kwargs.values()]
|
||||
return (values[:-1],)
|
||||
values = [str(value) for value in list(kwargs.values())[:-1]]
|
||||
return (values,)
|
||||
|
||||
|
||||
class ListAppend(ComfyNodeABC):
|
||||
|
||||
@@ -37,8 +37,8 @@ class SetCreate(ComfyNodeABC):
|
||||
FUNCTION = "create_set"
|
||||
|
||||
def create_set(self, **kwargs: list[Any]) -> tuple[set[Any]]:
|
||||
values = kwargs.values()
|
||||
return (set(values[:-1]),)
|
||||
values = list(kwargs.values())[:-1]
|
||||
return (set(values),)
|
||||
|
||||
|
||||
class SetCreateFromBoolean(ComfyNodeABC):
|
||||
@@ -63,8 +63,8 @@ class SetCreateFromBoolean(ComfyNodeABC):
|
||||
FUNCTION = "create_set"
|
||||
|
||||
def create_set(self, **kwargs: list[Any]) -> tuple[set[Any]]:
|
||||
values = [bool(value) for value in kwargs.values()]
|
||||
return (set(values[:-1]),)
|
||||
values = [bool(value) for value in list(kwargs.values())[:-1]]
|
||||
return (set(values),)
|
||||
|
||||
|
||||
class SetCreateFromFloat(ComfyNodeABC):
|
||||
@@ -89,8 +89,8 @@ class SetCreateFromFloat(ComfyNodeABC):
|
||||
FUNCTION = "create_set"
|
||||
|
||||
def create_set(self, **kwargs: list[Any]) -> tuple[set[Any]]:
|
||||
values = [float(value) for value in kwargs.values()]
|
||||
return (set(values[:-1]),)
|
||||
values = [float(value) for value in list(kwargs.values())[:-1]]
|
||||
return (set(values),)
|
||||
|
||||
|
||||
class SetCreateFromInt(ComfyNodeABC):
|
||||
@@ -115,8 +115,8 @@ class SetCreateFromInt(ComfyNodeABC):
|
||||
FUNCTION = "create_set"
|
||||
|
||||
def create_set(self, **kwargs: list[Any]) -> tuple[set[Any]]:
|
||||
values = [int(value) for value in kwargs.values()]
|
||||
return (set(values[:-1]),)
|
||||
values = [int(value) for value in list(kwargs.values())[:-1]]
|
||||
return (set(values),)
|
||||
|
||||
|
||||
class SetCreateFromString(ComfyNodeABC):
|
||||
@@ -141,8 +141,8 @@ class SetCreateFromString(ComfyNodeABC):
|
||||
FUNCTION = "create_set"
|
||||
|
||||
def create_set(self, **kwargs: list[Any]) -> tuple[set[Any]]:
|
||||
values = [str(value) for value in kwargs.values()]
|
||||
return (set(values[:-1]),)
|
||||
values = [str(value) for value in list(kwargs.values())[:-1]]
|
||||
return (set(values),)
|
||||
|
||||
|
||||
class SetAdd(ComfyNodeABC):
|
||||
|
||||
@@ -890,6 +890,74 @@ class StringRemovesuffix(ComfyNodeABC):
|
||||
return (string.removesuffix(suffix),)
|
||||
|
||||
|
||||
class StringUnescape(ComfyNodeABC):
|
||||
"""
|
||||
Unescapes a string by converting escape sequences to their actual characters.
|
||||
|
||||
This node converts escape sequences like '\n' (two characters) to actual newlines (one character),
|
||||
'\t' to tabs, '\\' to backslashes, etc. Useful for processing strings where escape sequences
|
||||
are represented literally rather than interpreted.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"string": (IO.STRING, {"default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (IO.STRING,)
|
||||
CATEGORY = "Basic/STRING"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "unescape"
|
||||
|
||||
def unescape(self, string):
|
||||
# Decode escaped sequences only for control characters
|
||||
result = (
|
||||
string
|
||||
.replace(r'\\', '\u0000')
|
||||
.replace(r'\n', '\n')
|
||||
.replace(r'\t', '\t')
|
||||
.replace(r'\"', '"')
|
||||
.replace(r'\'', "'")
|
||||
.replace('\u0000', '\\')
|
||||
)
|
||||
return (result,)
|
||||
|
||||
|
||||
class StringEscape(ComfyNodeABC):
|
||||
"""
|
||||
Escapes a string by converting special characters to escape sequences.
|
||||
|
||||
This node converts characters like newlines, tabs, quotes, and backslashes to their escaped
|
||||
representation (like '\n', '\t', '\"', '\\'). Useful when you need to prepare strings
|
||||
for formats that require escaped sequences instead of literal special characters.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"string": (IO.STRING, {"default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (IO.STRING,)
|
||||
CATEGORY = "Basic/STRING"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "escape"
|
||||
|
||||
def escape(self, string):
|
||||
result = (
|
||||
string
|
||||
.replace('\\', r'\\')
|
||||
.replace('\n', r'\n')
|
||||
.replace('\t', r'\t')
|
||||
.replace('"', r'\"')
|
||||
.replace("'", r"\'")
|
||||
)
|
||||
return (result,)
|
||||
|
||||
|
||||
class StringReplace(ComfyNodeABC):
|
||||
"""
|
||||
Replaces occurrences of a substring with another substring.
|
||||
@@ -1384,6 +1452,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"Basic data handling: StringDecode": StringDecode,
|
||||
"Basic data handling: StringEncode": StringEncode,
|
||||
"Basic data handling: StringEndswith": StringEndswith,
|
||||
"Basic data handling: StringEscape": StringEscape,
|
||||
"Basic data handling: StringExpandtabs": StringExpandtabs,
|
||||
"Basic data handling: StringFind": StringFind,
|
||||
"Basic data handling: StringFormatMap": StringFormatMap,
|
||||
@@ -1422,6 +1491,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"Basic data handling: StringStrip": StringStrip,
|
||||
"Basic data handling: StringSwapcase": StringSwapcase,
|
||||
"Basic data handling: StringTitle": StringTitle,
|
||||
"Basic data handling: StringUnescape": StringUnescape,
|
||||
"Basic data handling: StringUpper": StringUpper,
|
||||
"Basic data handling: StringZfill": StringZfill,
|
||||
}
|
||||
@@ -1435,6 +1505,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Basic data handling: StringDecode": "decode",
|
||||
"Basic data handling: StringEncode": "encode",
|
||||
"Basic data handling: StringEndswith": "endswith",
|
||||
"Basic data handling: StringEscape": "escape",
|
||||
"Basic data handling: StringExpandtabs": "expandtabs",
|
||||
"Basic data handling: StringFind": "find",
|
||||
"Basic data handling: StringFormatMap": "format_map",
|
||||
@@ -1473,6 +1544,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"Basic data handling: StringStrip": "strip",
|
||||
"Basic data handling: StringSwapcase": "swapcase",
|
||||
"Basic data handling: StringTitle": "title",
|
||||
"Basic data handling: StringUnescape": "unescape",
|
||||
"Basic data handling: StringUpper": "upper",
|
||||
"Basic data handling: StringZfill": "zfill",
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ def test_dict_set():
|
||||
def test_dict_create_from_boolean():
|
||||
node = DictCreateFromBoolean()
|
||||
# Test with dynamic inputs
|
||||
result = node.create(key_0="key1", value_0=True, key_1="key2", value_1=False)
|
||||
result = node.create(key_0="key1", value_0=True, key_1="key2", value_1=False, key_2="", value_2="")
|
||||
assert result == ({"key1": True, "key2": False},)
|
||||
# Test with empty inputs
|
||||
assert node.create() == ({},)
|
||||
@@ -64,7 +64,7 @@ def test_dict_create_from_boolean():
|
||||
def test_dict_create_from_float():
|
||||
node = DictCreateFromFloat()
|
||||
# Test with dynamic inputs
|
||||
result = node.create(key_0="key1", value_0=1.5, key_1="key2", value_1=2.5)
|
||||
result = node.create(key_0="key1", value_0=1.5, key_1="key2", value_1=2.5, key_2="", value_2="")
|
||||
assert result == ({"key1": 1.5, "key2": 2.5},)
|
||||
# Test with empty inputs
|
||||
assert node.create() == ({},)
|
||||
@@ -73,7 +73,7 @@ def test_dict_create_from_float():
|
||||
def test_dict_create_from_int():
|
||||
node = DictCreateFromInt()
|
||||
# Test with dynamic inputs
|
||||
result = node.create(key_0="key1", value_0=1, key_1="key2", value_1=2)
|
||||
result = node.create(key_0="key1", value_0=1, key_1="key2", value_1=2, key_2="", value_2="")
|
||||
assert result == ({"key1": 1, "key2": 2},)
|
||||
# Test with empty inputs
|
||||
assert node.create() == ({},)
|
||||
@@ -82,7 +82,7 @@ def test_dict_create_from_int():
|
||||
def test_dict_create_from_string():
|
||||
node = DictCreateFromString()
|
||||
# Test with dynamic inputs
|
||||
result = node.create(key_0="key1", value_0="value1", key_1="key2", value_1="value2")
|
||||
result = node.create(key_0="key1", value_0="value1", key_1="key2", value_1="value2", key_2="", value_2="")
|
||||
assert result == ({"key1": "value1", "key2": "value2"},)
|
||||
# Test with empty inputs
|
||||
assert node.create() == ({},)
|
||||
|
||||
+10
-10
@@ -176,36 +176,36 @@ def test_list_max():
|
||||
|
||||
def test_list_create():
|
||||
node = ListCreate()
|
||||
assert node.create_list(item_0=1, item_1=2, item_2=3) == ([1, 2, 3],)
|
||||
assert node.create_list(item_0=1, item_1=2, item_2=3, item_3="") == ([1, 2, 3],)
|
||||
assert node.create_list() == ([],) # Empty list
|
||||
assert node.create_list(item_0="test") == (["test"],)
|
||||
assert node.create_list(item_0="test", item_1="") == (["test"],)
|
||||
|
||||
|
||||
def test_list_create_from_boolean():
|
||||
node = ListCreateFromBoolean()
|
||||
assert node.create_list(item_0=True, item_1=False, item_2=True) == ([True, False, True],)
|
||||
assert node.create_list(item_0=True) == ([True],)
|
||||
assert node.create_list(item_0=True, item_1=False, item_2=True, item_3="") == ([True, False, True],)
|
||||
assert node.create_list(item_0=True, item_1="") == ([True],)
|
||||
assert node.create_list() == ([],)
|
||||
|
||||
|
||||
def test_list_create_from_float():
|
||||
node = ListCreateFromFloat()
|
||||
assert node.create_list(item_0=1.1, item_1=2.2, item_2=3.3) == ([1.1, 2.2, 3.3],)
|
||||
assert node.create_list(item_0=0.0) == ([0.0],)
|
||||
assert node.create_list(item_0=1.1, item_1=2.2, item_2=3.3, item_3="") == ([1.1, 2.2, 3.3],)
|
||||
assert node.create_list(item_0=0.0, item_1="") == ([0.0],)
|
||||
assert node.create_list() == ([],)
|
||||
|
||||
|
||||
def test_list_create_from_int():
|
||||
node = ListCreateFromInt()
|
||||
assert node.create_list(item_0=1, item_1=2, item_2=3) == ([1, 2, 3],)
|
||||
assert node.create_list(item_0=0) == ([0],)
|
||||
assert node.create_list(item_0=1, item_1=2, item_2=3, item_3="") == ([1, 2, 3],)
|
||||
assert node.create_list(item_0=0, item_1="") == ([0],)
|
||||
assert node.create_list() == ([],)
|
||||
|
||||
|
||||
def test_list_create_from_string():
|
||||
node = ListCreateFromString()
|
||||
assert node.create_list(item_0="a", item_1="b", item_2="c") == (["a", "b", "c"],)
|
||||
assert node.create_list(item_0="test") == (["test"],)
|
||||
assert node.create_list(item_0="a", item_1="b", item_2="c", item_3="") == (["a", "b", "c"],)
|
||||
assert node.create_list(item_0="test", item_1="") == (["test"],)
|
||||
assert node.create_list() == ([],)
|
||||
|
||||
|
||||
|
||||
+14
-14
@@ -26,29 +26,29 @@ from src.basic_data_handling.set_nodes import (
|
||||
def test_set_create():
|
||||
node = SetCreate()
|
||||
# Testing with kwargs to simulate dynamic inputs
|
||||
assert node.create_set(item_0=1, item_1=2, item_2=3) == ({1, 2, 3},)
|
||||
assert node.create_set(item_0="a", item_1="b") == ({"a", "b"},)
|
||||
assert node.create_set(item_0=1, item_1=2, item_2=3, item_3="") == ({1, 2, 3},)
|
||||
assert node.create_set(item_0="a", item_1="b", item_2="") == ({"a", "b"},)
|
||||
assert node.create_set() == (set(),) # Empty set with no arguments
|
||||
# Mixed types
|
||||
assert node.create_set(item_0=1, item_1="b", item_2=True) == ({1, "b", True},)
|
||||
assert node.create_set(item_0=1, item_1="b", item_2=True, item_3="") == ({1, "b", True},)
|
||||
|
||||
|
||||
def test_set_create_from_int():
|
||||
node = SetCreateFromInt()
|
||||
assert node.create_set(item_0=1, item_1=2, item_2=3) == ({1, 2, 3},)
|
||||
assert node.create_set(item_0=5) == ({5},) # Single item set
|
||||
assert node.create_set(item_0=1, item_1=1) == ({1},) # Duplicate items become single item
|
||||
assert node.create_set(item_0=1, item_1=2, item_2=3, item_3="") == ({1, 2, 3},)
|
||||
assert node.create_set(item_0=5, item_1="") == ({5},) # Single item set
|
||||
assert node.create_set(item_0=1, item_1=1, item_2="") == ({1},) # Duplicate items become single item
|
||||
assert node.create_set() == (set(),) # Empty set with no arguments
|
||||
|
||||
|
||||
def test_set_create_from_string():
|
||||
node = SetCreateFromString()
|
||||
result = node.create_set(item_0="apple", item_1="banana")
|
||||
result = node.create_set(item_0="apple", item_1="banana", item_2="")
|
||||
assert isinstance(result[0], set)
|
||||
assert result[0] == {"apple", "banana"}
|
||||
|
||||
# Duplicate strings
|
||||
result = node.create_set(item_0="apple", item_1="apple")
|
||||
result = node.create_set(item_0="apple", item_1="apple", item_2="")
|
||||
assert result[0] == {"apple"}
|
||||
|
||||
# Empty set
|
||||
@@ -57,19 +57,19 @@ def test_set_create_from_string():
|
||||
|
||||
def test_set_create_from_float():
|
||||
node = SetCreateFromFloat()
|
||||
assert node.create_set(item_0=1.5, item_1=2.5) == ({1.5, 2.5},)
|
||||
assert node.create_set(item_0=3.14) == ({3.14},) # Single item set
|
||||
assert node.create_set(item_0=1.0, item_1=1.0) == ({1.0},) # Duplicate items
|
||||
assert node.create_set(item_0=1.5, item_1=2.5, item_2="") == ({1.5, 2.5},)
|
||||
assert node.create_set(item_0=3.14, item_1="") == ({3.14},) # Single item set
|
||||
assert node.create_set(item_0=1.0, item_1=1.0, item_2="") == ({1.0},) # Duplicate items
|
||||
assert node.create_set() == (set(),) # Empty set with no arguments
|
||||
|
||||
|
||||
def test_set_create_from_boolean():
|
||||
node = SetCreateFromBoolean()
|
||||
assert node.create_set(item_0=True, item_1=False) == ({True, False},)
|
||||
assert node.create_set(item_0=True, item_1=True) == ({True},) # Duplicate booleans
|
||||
assert node.create_set(item_0=True, item_1=False, item_2="") == ({True, False},)
|
||||
assert node.create_set(item_0=True, item_1=True, item_2="") == ({True},) # Duplicate booleans
|
||||
assert node.create_set() == (set(),) # Empty set with no arguments
|
||||
# Test conversion from non-boolean values
|
||||
assert node.create_set(item_0=1, item_1=0) == ({True, False},)
|
||||
assert node.create_set(item_0=1, item_1=0, item_2="") == ({True, False},)
|
||||
|
||||
|
||||
def test_set_add():
|
||||
|
||||
@@ -10,6 +10,7 @@ from src.basic_data_handling.string_nodes import (
|
||||
StringDecode,
|
||||
StringEncode,
|
||||
StringEndswith,
|
||||
StringEscape,
|
||||
StringExpandtabs,
|
||||
StringFind,
|
||||
StringFormatMap,
|
||||
@@ -47,6 +48,7 @@ from src.basic_data_handling.string_nodes import (
|
||||
StringStrip,
|
||||
StringSwapcase,
|
||||
StringTitle,
|
||||
StringUnescape,
|
||||
StringUpper,
|
||||
StringZfill,
|
||||
)
|
||||
@@ -364,6 +366,38 @@ def test_zfill():
|
||||
assert node.zfill("123", 2) == ("123",) # Width smaller than string length
|
||||
assert node.zfill("", 3) == ("000",) # Empty string
|
||||
|
||||
def test_unescape():
|
||||
node = StringUnescape()
|
||||
# Test control characters
|
||||
assert node.unescape(r"Hello\nWorld") == ("Hello\nWorld",) # Newline
|
||||
assert node.unescape(r"Hello\tWorld") == ("Hello\tWorld",) # Tab
|
||||
assert node.unescape(r"C:\\Program Files\\App") == (r"C:\Program Files\App",) # Backslash
|
||||
assert node.unescape(r"Quote: \"Text\"") == ("Quote: \"Text\"",) # Double quote
|
||||
assert node.unescape(r"Quote: \'Text\'") == ("Quote: 'Text'",) # Single quote
|
||||
|
||||
# Test that normal Unicode characters remain unchanged
|
||||
assert node.unescape("German: äöüß") == ("German: äöüß",) # Umlauts should be preserved
|
||||
assert node.unescape("Hello äöü\nWorld") == ("Hello äöü\nWorld",) # Umlauts with control char
|
||||
|
||||
# Test complex string with multiple escape sequences
|
||||
assert node.unescape(r"Path: C:\\folder\\file.txt\nLine1\tLine2") == ("Path: C:\\folder\\file.txt\nLine1\tLine2",)
|
||||
|
||||
def test_escape():
|
||||
node = StringEscape()
|
||||
# Test control characters
|
||||
assert node.escape("Hello\nWorld") == (r"Hello\nWorld",) # Newline
|
||||
assert node.escape("Hello\tWorld") == (r"Hello\tWorld",) # Tab
|
||||
assert node.escape(r"C:\Program Files\App") == (r"C:\\Program Files\\App",) # Backslash
|
||||
assert node.escape('Quote: "Text"') == (r'Quote: \"Text\"',) # Double quote
|
||||
assert node.escape("Quote: 'Text'") == (r"Quote: \'Text\'",) # Single quote
|
||||
|
||||
# Test that normal Unicode characters remain unchanged
|
||||
assert node.escape("German: äöüß") == (r"German: äöüß",) # Umlauts should be preserved
|
||||
assert node.escape("Hello äöü\nWorld") == (r"Hello äöü\nWorld",) # Umlauts with control char
|
||||
|
||||
# Test complex string with multiple special characters
|
||||
assert node.escape("Path: C:\\folder\\file.txt\nLine1\tLine2") == (r"Path: C:\\folder\\file.txt\nLine1\tLine2",)
|
||||
|
||||
def test_length():
|
||||
node = StringLength()
|
||||
assert node.length("hello") == (5,)
|
||||
|
||||
Reference in New Issue
Block a user