mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-09 13:50:49 +08:00
179 lines
4.9 KiB
Python
179 lines
4.9 KiB
Python
import inspect
|
|
import operator
|
|
from typing import OrderedDict, Callable, Any
|
|
|
|
from comfy.comfy_types import IO
|
|
from comfy.lazy_helpers import is_input_pending
|
|
from comfy.node_helpers import export_custom_nodes
|
|
from comfy.nodes.package_typing import CustomNode, InputTypes
|
|
|
|
|
|
def takes_n_args(obj, n):
|
|
if not callable(obj):
|
|
return False
|
|
|
|
try:
|
|
sig = inspect.signature(obj)
|
|
except (TypeError, ValueError):
|
|
return False
|
|
|
|
params = sig.parameters.values()
|
|
|
|
named_param_count = sum(
|
|
1 for p in params
|
|
if p.kind not in (inspect.Parameter.VAR_POSITIONAL,
|
|
inspect.Parameter.VAR_KEYWORD)
|
|
)
|
|
|
|
return named_param_count == n
|
|
|
|
|
|
_BINARY_OPS: dict[str, Callable[[Any, Any], Any]] = OrderedDict({
|
|
**{op: getattr(operator, op) for op in dir(operator) if takes_n_args(getattr(operator, op), 2)},
|
|
"and": lambda a, b: a and b,
|
|
"or": lambda a, b: a or b,
|
|
})
|
|
_UNARY_OPS: dict[str, Callable[[Any], Any]] = {
|
|
**{op: getattr(operator, op) for op in dir(operator) if takes_n_args(getattr(operator, op), 1)},
|
|
"not": lambda a: not a
|
|
}
|
|
|
|
|
|
class IsNone(CustomNode):
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputTypes:
|
|
return {
|
|
"required": {},
|
|
"optional": {
|
|
"value": (IO.ANY, {}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = (IO.BOOLEAN,)
|
|
FUNCTION = "execute"
|
|
CATEGORY = "logic"
|
|
|
|
def execute(self, value=None):
|
|
return (value is None,)
|
|
|
|
|
|
class LazySwitch(CustomNode):
|
|
"""
|
|
sherlocked from KJ nodes with fixes
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"switch": ("BOOLEAN",),
|
|
},
|
|
"optional": {
|
|
"on_false": (IO.ANY, {"lazy": True}),
|
|
"on_true": (IO.ANY, {"lazy": True}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = (IO.ANY,)
|
|
FUNCTION = "execute"
|
|
CATEGORY = "logic"
|
|
DESCRIPTION = "Controls flow of execution based on a boolean switch."
|
|
|
|
def check_lazy_status(self, switch, on_false=None, on_true=None):
|
|
try:
|
|
on_false_not_evaluated, on_true_not_evaluated = is_input_pending("on_false", "on_true")
|
|
except LookupError:
|
|
on_false_not_evaluated, on_true_not_evaluated = on_false is None, on_true is None
|
|
if switch and on_true_not_evaluated:
|
|
return ["on_true"]
|
|
if not switch and on_false_not_evaluated:
|
|
return ["on_false"]
|
|
|
|
def execute(self, switch, on_false=None, on_true=None):
|
|
value = on_true if switch else on_false
|
|
return (value,)
|
|
|
|
|
|
class UnaryOperation(CustomNode):
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputTypes:
|
|
return {
|
|
"required": {},
|
|
"optional": {
|
|
"value": (IO.ANY, {}),
|
|
"op": (list(_UNARY_OPS.keys()), {"default": "not"})
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = (IO.ANY,)
|
|
FUNCTION = "execute"
|
|
CATEGORY = "logic"
|
|
|
|
def execute(self, value, op):
|
|
return _UNARY_OPS[op](value),
|
|
|
|
|
|
class BooleanUnaryOperation(UnaryOperation):
|
|
RETURN_TYPES = (IO.BOOLEAN,)
|
|
|
|
def execute(self, value, op):
|
|
return bool(super().execute(value, op)[0]),
|
|
|
|
|
|
class BinaryOperation(CustomNode):
|
|
@classmethod
|
|
def INPUT_TYPES(cls) -> InputTypes:
|
|
return {
|
|
"required": {},
|
|
"optional": OrderedDict({
|
|
"lhs": (IO.ANY, {"lazy": True}),
|
|
"op": (list(_BINARY_OPS.keys()), {"default": "eq"}),
|
|
"rhs": (IO.ANY, {"lazy": True}),
|
|
})
|
|
}
|
|
|
|
RETURN_TYPES = (IO.ANY,)
|
|
FUNCTION = "execute"
|
|
CATEGORY = "logic"
|
|
DESCRIPTION = ""
|
|
|
|
def check_lazy_status(self, lhs=None, op=None, rhs=None) -> list[str]:
|
|
try:
|
|
lhs_not_evaluated, rhs_not_evaluated = is_input_pending("lhs", "rhs")
|
|
except LookupError:
|
|
lhs_not_evaluated, rhs_not_evaluated = lhs is None, rhs is None
|
|
lhs_evaluated, rhs_evaluated = not lhs_not_evaluated, not rhs_not_evaluated
|
|
match op:
|
|
case "and":
|
|
if lhs_not_evaluated:
|
|
return ["lhs"]
|
|
if lhs_evaluated and lhs is not False and rhs_not_evaluated:
|
|
return ["rhs"]
|
|
return []
|
|
case "or":
|
|
if lhs_not_evaluated:
|
|
return ["lhs"]
|
|
if lhs_evaluated and lhs is not True and rhs_not_evaluated:
|
|
return ["rhs"]
|
|
return []
|
|
case _:
|
|
to_eval = []
|
|
if lhs_not_evaluated:
|
|
to_eval.append("lhs")
|
|
if rhs_not_evaluated:
|
|
to_eval.append("rhs")
|
|
return to_eval
|
|
|
|
def execute(self, lhs, op, rhs):
|
|
return _BINARY_OPS[op](lhs, rhs),
|
|
|
|
|
|
class BooleanBinaryOperation(BinaryOperation):
|
|
RETURN_TYPES = (IO.BOOLEAN,)
|
|
|
|
def execute(self, lhs, op, rhs):
|
|
return bool(super().execute(lhs, op, rhs)[0]),
|
|
|
|
|
|
export_custom_nodes()
|