mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-09-10 03:25:22 +00:00
convert String nodes to V3 schema (#9370)
This commit is contained in:
@@ -1,77 +1,91 @@
|
|||||||
import re
|
import re
|
||||||
|
from typing_extensions import override
|
||||||
|
|
||||||
from comfy.comfy_types.node_typing import IO
|
from comfy_api.latest import ComfyExtension, io
|
||||||
|
|
||||||
class StringConcatenate():
|
|
||||||
|
class StringConcatenate(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def define_schema(cls):
|
||||||
return {
|
return io.Schema(
|
||||||
"required": {
|
node_id="StringConcatenate",
|
||||||
"string_a": (IO.STRING, {"multiline": True}),
|
display_name="Concatenate",
|
||||||
"string_b": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"delimiter": (IO.STRING, {"multiline": False, "default": ""})
|
inputs=[
|
||||||
}
|
io.String.Input("string_a", multiline=True),
|
||||||
}
|
io.String.Input("string_b", multiline=True),
|
||||||
|
io.String.Input("delimiter", multiline=False, default=""),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string_a, string_b, delimiter, **kwargs):
|
|
||||||
return delimiter.join((string_a, string_b)),
|
|
||||||
|
|
||||||
class StringSubstring():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def execute(cls, string_a, string_b, delimiter):
|
||||||
return {
|
return io.NodeOutput(delimiter.join((string_a, string_b)))
|
||||||
"required": {
|
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
|
||||||
"start": (IO.INT, {}),
|
|
||||||
"end": (IO.INT, {}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, start, end, **kwargs):
|
class StringSubstring(io.ComfyNode):
|
||||||
return string[start:end],
|
|
||||||
|
|
||||||
class StringLength():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def define_schema(cls):
|
||||||
return {
|
return io.Schema(
|
||||||
"required": {
|
node_id="StringSubstring",
|
||||||
"string": (IO.STRING, {"multiline": True})
|
display_name="Substring",
|
||||||
}
|
category="utils/string",
|
||||||
}
|
inputs=[
|
||||||
|
io.String.Input("string", multiline=True),
|
||||||
|
io.Int.Input("start"),
|
||||||
|
io.Int.Input("end"),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.INT,)
|
|
||||||
RETURN_NAMES = ("length",)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, **kwargs):
|
|
||||||
length = len(string)
|
|
||||||
|
|
||||||
return length,
|
|
||||||
|
|
||||||
class CaseConverter():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def execute(cls, string, start, end):
|
||||||
return {
|
return io.NodeOutput(string[start:end])
|
||||||
"required": {
|
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
|
||||||
"mode": (IO.COMBO, {"options": ["UPPERCASE", "lowercase", "Capitalize", "Title Case"]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, mode, **kwargs):
|
class StringLength(io.ComfyNode):
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return io.Schema(
|
||||||
|
node_id="StringLength",
|
||||||
|
display_name="Length",
|
||||||
|
category="utils/string",
|
||||||
|
inputs=[
|
||||||
|
io.String.Input("string", multiline=True),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.Int.Output(display_name="length"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def execute(cls, string):
|
||||||
|
return io.NodeOutput(len(string))
|
||||||
|
|
||||||
|
|
||||||
|
class CaseConverter(io.ComfyNode):
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return io.Schema(
|
||||||
|
node_id="CaseConverter",
|
||||||
|
display_name="Case Converter",
|
||||||
|
category="utils/string",
|
||||||
|
inputs=[
|
||||||
|
io.String.Input("string", multiline=True),
|
||||||
|
io.Combo.Input("mode", options=["UPPERCASE", "lowercase", "Capitalize", "Title Case"]),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def execute(cls, string, mode):
|
||||||
if mode == "UPPERCASE":
|
if mode == "UPPERCASE":
|
||||||
result = string.upper()
|
result = string.upper()
|
||||||
elif mode == "lowercase":
|
elif mode == "lowercase":
|
||||||
@@ -83,24 +97,27 @@ class CaseConverter():
|
|||||||
else:
|
else:
|
||||||
result = string
|
result = string
|
||||||
|
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
class StringTrim():
|
class StringTrim(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def define_schema(cls):
|
||||||
return {
|
return io.Schema(
|
||||||
"required": {
|
node_id="StringTrim",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="Trim",
|
||||||
"mode": (IO.COMBO, {"options": ["Both", "Left", "Right"]})
|
category="utils/string",
|
||||||
}
|
inputs=[
|
||||||
}
|
io.String.Input("string", multiline=True),
|
||||||
|
io.Combo.Input("mode", options=["Both", "Left", "Right"]),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(cls, string, mode):
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, mode, **kwargs):
|
|
||||||
if mode == "Both":
|
if mode == "Both":
|
||||||
result = string.strip()
|
result = string.strip()
|
||||||
elif mode == "Left":
|
elif mode == "Left":
|
||||||
@@ -110,70 +127,78 @@ class StringTrim():
|
|||||||
else:
|
else:
|
||||||
result = string
|
result = string
|
||||||
|
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
class StringReplace():
|
|
||||||
|
class StringReplace(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def define_schema(cls):
|
||||||
return {
|
return io.Schema(
|
||||||
"required": {
|
node_id="StringReplace",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="Replace",
|
||||||
"find": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"replace": (IO.STRING, {"multiline": True})
|
inputs=[
|
||||||
}
|
io.String.Input("string", multiline=True),
|
||||||
}
|
io.String.Input("find", multiline=True),
|
||||||
|
io.String.Input("replace", multiline=True),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, find, replace, **kwargs):
|
|
||||||
result = string.replace(find, replace)
|
|
||||||
return result,
|
|
||||||
|
|
||||||
|
|
||||||
class StringContains():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def execute(cls, string, find, replace):
|
||||||
return {
|
return io.NodeOutput(string.replace(find, replace))
|
||||||
"required": {
|
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
|
||||||
"substring": (IO.STRING, {"multiline": True}),
|
|
||||||
"case_sensitive": (IO.BOOLEAN, {"default": True})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = (IO.BOOLEAN,)
|
|
||||||
RETURN_NAMES = ("contains",)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, substring, case_sensitive, **kwargs):
|
class StringContains(io.ComfyNode):
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return io.Schema(
|
||||||
|
node_id="StringContains",
|
||||||
|
display_name="Contains",
|
||||||
|
category="utils/string",
|
||||||
|
inputs=[
|
||||||
|
io.String.Input("string", multiline=True),
|
||||||
|
io.String.Input("substring", multiline=True),
|
||||||
|
io.Boolean.Input("case_sensitive", default=True),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.Boolean.Output(display_name="contains"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def execute(cls, string, substring, case_sensitive):
|
||||||
if case_sensitive:
|
if case_sensitive:
|
||||||
contains = substring in string
|
contains = substring in string
|
||||||
else:
|
else:
|
||||||
contains = substring.lower() in string.lower()
|
contains = substring.lower() in string.lower()
|
||||||
|
|
||||||
return contains,
|
return io.NodeOutput(contains)
|
||||||
|
|
||||||
|
|
||||||
class StringCompare():
|
class StringCompare(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def define_schema(cls):
|
||||||
return {
|
return io.Schema(
|
||||||
"required": {
|
node_id="StringCompare",
|
||||||
"string_a": (IO.STRING, {"multiline": True}),
|
display_name="Compare",
|
||||||
"string_b": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"mode": (IO.COMBO, {"options": ["Starts With", "Ends With", "Equal"]}),
|
inputs=[
|
||||||
"case_sensitive": (IO.BOOLEAN, {"default": True})
|
io.String.Input("string_a", multiline=True),
|
||||||
}
|
io.String.Input("string_b", multiline=True),
|
||||||
}
|
io.Combo.Input("mode", options=["Starts With", "Ends With", "Equal"]),
|
||||||
|
io.Boolean.Input("case_sensitive", default=True),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.Boolean.Output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.BOOLEAN,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(cls, string_a, string_b, mode, case_sensitive):
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string_a, string_b, mode, case_sensitive, **kwargs):
|
|
||||||
if case_sensitive:
|
if case_sensitive:
|
||||||
a = string_a
|
a = string_a
|
||||||
b = string_b
|
b = string_b
|
||||||
@@ -182,31 +207,34 @@ class StringCompare():
|
|||||||
b = string_b.lower()
|
b = string_b.lower()
|
||||||
|
|
||||||
if mode == "Equal":
|
if mode == "Equal":
|
||||||
return a == b,
|
return io.NodeOutput(a == b)
|
||||||
elif mode == "Starts With":
|
elif mode == "Starts With":
|
||||||
return a.startswith(b),
|
return io.NodeOutput(a.startswith(b))
|
||||||
elif mode == "Ends With":
|
elif mode == "Ends With":
|
||||||
return a.endswith(b),
|
return io.NodeOutput(a.endswith(b))
|
||||||
|
|
||||||
class RegexMatch():
|
|
||||||
|
class RegexMatch(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def define_schema(cls):
|
||||||
return {
|
return io.Schema(
|
||||||
"required": {
|
node_id="RegexMatch",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="Regex Match",
|
||||||
"regex_pattern": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"case_insensitive": (IO.BOOLEAN, {"default": True}),
|
inputs=[
|
||||||
"multiline": (IO.BOOLEAN, {"default": False}),
|
io.String.Input("string", multiline=True),
|
||||||
"dotall": (IO.BOOLEAN, {"default": False})
|
io.String.Input("regex_pattern", multiline=True),
|
||||||
}
|
io.Boolean.Input("case_insensitive", default=True),
|
||||||
}
|
io.Boolean.Input("multiline", default=False),
|
||||||
|
io.Boolean.Input("dotall", default=False),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.Boolean.Output(display_name="matches"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.BOOLEAN,)
|
@classmethod
|
||||||
RETURN_NAMES = ("matches",)
|
def execute(cls, string, regex_pattern, case_insensitive, multiline, dotall):
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, regex_pattern, case_insensitive, multiline, dotall, **kwargs):
|
|
||||||
flags = 0
|
flags = 0
|
||||||
|
|
||||||
if case_insensitive:
|
if case_insensitive:
|
||||||
@@ -223,29 +251,32 @@ class RegexMatch():
|
|||||||
except re.error:
|
except re.error:
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
class RegexExtract():
|
class RegexExtract(io.ComfyNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def define_schema(cls):
|
||||||
return {
|
return io.Schema(
|
||||||
"required": {
|
node_id="RegexExtract",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="Regex Extract",
|
||||||
"regex_pattern": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"mode": (IO.COMBO, {"options": ["First Match", "All Matches", "First Group", "All Groups"]}),
|
inputs=[
|
||||||
"case_insensitive": (IO.BOOLEAN, {"default": True}),
|
io.String.Input("string", multiline=True),
|
||||||
"multiline": (IO.BOOLEAN, {"default": False}),
|
io.String.Input("regex_pattern", multiline=True),
|
||||||
"dotall": (IO.BOOLEAN, {"default": False}),
|
io.Combo.Input("mode", options=["First Match", "All Matches", "First Group", "All Groups"]),
|
||||||
"group_index": (IO.INT, {"default": 1, "min": 0, "max": 100})
|
io.Boolean.Input("case_insensitive", default=True),
|
||||||
}
|
io.Boolean.Input("multiline", default=False),
|
||||||
}
|
io.Boolean.Input("dotall", default=False),
|
||||||
|
io.Int.Input("group_index", default=1, min=0, max=100),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(cls, string, regex_pattern, mode, case_insensitive, multiline, dotall, group_index):
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, regex_pattern, mode, case_insensitive, multiline, dotall, group_index, **kwargs):
|
|
||||||
join_delimiter = "\n"
|
join_delimiter = "\n"
|
||||||
|
|
||||||
flags = 0
|
flags = 0
|
||||||
@@ -294,32 +325,33 @@ class RegexExtract():
|
|||||||
except re.error:
|
except re.error:
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
class RegexReplace():
|
class RegexReplace(io.ComfyNode):
|
||||||
DESCRIPTION = "Find and replace text using regex patterns."
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def define_schema(cls):
|
||||||
return {
|
return io.Schema(
|
||||||
"required": {
|
node_id="RegexReplace",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="Regex Replace",
|
||||||
"regex_pattern": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"replace": (IO.STRING, {"multiline": True}),
|
description="Find and replace text using regex patterns.",
|
||||||
},
|
inputs=[
|
||||||
"optional": {
|
io.String.Input("string", multiline=True),
|
||||||
"case_insensitive": (IO.BOOLEAN, {"default": True}),
|
io.String.Input("regex_pattern", multiline=True),
|
||||||
"multiline": (IO.BOOLEAN, {"default": False}),
|
io.String.Input("replace", multiline=True),
|
||||||
"dotall": (IO.BOOLEAN, {"default": False, "tooltip": "When enabled, the dot (.) character will match any character including newline characters. When disabled, dots won't match newlines."}),
|
io.Boolean.Input("case_insensitive", default=True, optional=True),
|
||||||
"count": (IO.INT, {"default": 0, "min": 0, "max": 100, "tooltip": "Maximum number of replacements to make. Set to 0 to replace all occurrences (default). Set to 1 to replace only the first match, 2 for the first two matches, etc."}),
|
io.Boolean.Input("multiline", default=False, optional=True),
|
||||||
}
|
io.Boolean.Input("dotall", default=False, optional=True, tooltip="When enabled, the dot (.) character will match any character including newline characters. When disabled, dots won't match newlines."),
|
||||||
}
|
io.Int.Input("count", default=0, min=0, max=100, optional=True, tooltip="Maximum number of replacements to make. Set to 0 to replace all occurrences (default). Set to 1 to replace only the first match, 2 for the first two matches, etc."),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(cls, string, regex_pattern, replace, case_insensitive=True, multiline=False, dotall=False, count=0):
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, regex_pattern, replace, case_insensitive=True, multiline=False, dotall=False, count=0, **kwargs):
|
|
||||||
flags = 0
|
flags = 0
|
||||||
|
|
||||||
if case_insensitive:
|
if case_insensitive:
|
||||||
@@ -329,32 +361,25 @@ class RegexReplace():
|
|||||||
if dotall:
|
if dotall:
|
||||||
flags |= re.DOTALL
|
flags |= re.DOTALL
|
||||||
result = re.sub(regex_pattern, replace, string, count=count, flags=flags)
|
result = re.sub(regex_pattern, replace, string, count=count, flags=flags)
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
|
||||||
"StringConcatenate": StringConcatenate,
|
|
||||||
"StringSubstring": StringSubstring,
|
|
||||||
"StringLength": StringLength,
|
|
||||||
"CaseConverter": CaseConverter,
|
|
||||||
"StringTrim": StringTrim,
|
|
||||||
"StringReplace": StringReplace,
|
|
||||||
"StringContains": StringContains,
|
|
||||||
"StringCompare": StringCompare,
|
|
||||||
"RegexMatch": RegexMatch,
|
|
||||||
"RegexExtract": RegexExtract,
|
|
||||||
"RegexReplace": RegexReplace,
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
class StringExtension(ComfyExtension):
|
||||||
"StringConcatenate": "Concatenate",
|
@override
|
||||||
"StringSubstring": "Substring",
|
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
||||||
"StringLength": "Length",
|
return [
|
||||||
"CaseConverter": "Case Converter",
|
StringConcatenate,
|
||||||
"StringTrim": "Trim",
|
StringSubstring,
|
||||||
"StringReplace": "Replace",
|
StringLength,
|
||||||
"StringContains": "Contains",
|
CaseConverter,
|
||||||
"StringCompare": "Compare",
|
StringTrim,
|
||||||
"RegexMatch": "Regex Match",
|
StringReplace,
|
||||||
"RegexExtract": "Regex Extract",
|
StringContains,
|
||||||
"RegexReplace": "Regex Replace",
|
StringCompare,
|
||||||
}
|
RegexMatch,
|
||||||
|
RegexExtract,
|
||||||
|
RegexReplace,
|
||||||
|
]
|
||||||
|
|
||||||
|
async def comfy_entrypoint() -> StringExtension:
|
||||||
|
return StringExtension()
|
||||||
|
Reference in New Issue
Block a user