Compare commits

..

1 Commits

4 changed files with 85 additions and 4 deletions

View File

@ -30,6 +30,46 @@ from comfy_execution.graph_utils import ExecutionBlocker
from ._util import MESH, VOXEL, SVG as _SVG, File3D
class EmptyInputSentinel:
"""
Sentinel class indicating an empty/missing input.
Use the class itself (not an instance) as the sentinel.
Compare using 'is' or 'is not' only.
"""
def __new__(cls):
raise TypeError("EmptyInputSentinel cannot be instantiated, use the class itself")
def __init_subclass__(cls, **kwargs):
raise TypeError("EmptyInputSentinel cannot be subclassed")
@classmethod
def __class_getitem__(cls, item):
raise TypeError("EmptyInputSentinel cannot be subscripted")
def __repr__(self):
return "<EmptyInput>"
def __bool__(self):
raise TypeError("EmptyInputSentinel cannot be used in boolean context")
def __eq__(self, other):
raise TypeError("EmptyInputSentinel cannot be compared with ==, use 'is' instead")
def __ne__(self, other):
raise TypeError("EmptyInputSentinel cannot be compared with !=, use 'is not' instead")
def __hash__(self):
raise TypeError("EmptyInputSentinel cannot be hashed")
def __iter__(self):
raise TypeError("EmptyInputSentinel cannot be iterated")
def __len__(self):
raise TypeError("EmptyInputSentinel has no length")
class FolderType(str, Enum):
input = "input"
output = "output"
@ -2110,6 +2150,7 @@ __all__ = [
"DynamicCombo",
"Autogrow",
# Other classes
"EmptyInputSentinel",
"HiddenHolder",
"Hidden",
"NodeInfoV1",

View File

@ -143,9 +143,9 @@ async def poll_op(
poll_interval: float = 5.0,
max_poll_attempts: int = 160,
timeout_per_poll: float = 120.0,
max_retries_per_poll: int = 10,
max_retries_per_poll: int = 3,
retry_delay_per_poll: float = 1.0,
retry_backoff_per_poll: float = 1.4,
retry_backoff_per_poll: float = 2.0,
estimated_duration: int | None = None,
cancel_endpoint: ApiEndpoint | None = None,
cancel_timeout: float = 10.0,
@ -240,9 +240,9 @@ async def poll_op_raw(
poll_interval: float = 5.0,
max_poll_attempts: int = 160,
timeout_per_poll: float = 120.0,
max_retries_per_poll: int = 10,
max_retries_per_poll: int = 3,
retry_delay_per_poll: float = 1.0,
retry_backoff_per_poll: float = 1.4,
retry_backoff_per_poll: float = 2.0,
estimated_duration: int | None = None,
cancel_endpoint: ApiEndpoint | None = None,
cancel_timeout: float = 10.0,

View File

@ -91,6 +91,41 @@ class SoftSwitchNode(io.ComfyNode):
return io.NodeOutput(on_true if switch else on_false)
class OptionalSwitchNode(io.ComfyNode):
@classmethod
def define_schema(cls):
template = io.MatchType.Template("switch")
return io.Schema(
node_id="ComfyOptionalSwitchNode",
display_name="Optional Switch",
category="logic",
is_experimental=True,
inputs=[
io.Boolean.Input("switch"),
io.MatchType.Input("on_false", template=template, lazy=True, optional=True),
io.MatchType.Input("on_true", template=template, lazy=True, optional=True),
],
outputs=[
io.MatchType.Output(template=template, display_name="output"),
],
)
@classmethod
def check_lazy_status(cls, switch, on_false=MISSING, on_true=MISSING):
# Only evaluate the input that corresponds to the switch value
if switch and on_true is None:
return ["on_true"]
if not switch and on_false is None:
return ["on_false"]
@classmethod
def execute(cls, switch, on_true=MISSING, on_false=MISSING) -> io.NodeOutput:
selected = on_true if switch else on_false
if selected is MISSING:
return io.NodeOutput(io.EmptyInputSentinel)
return io.NodeOutput(selected)
class CustomComboNode(io.ComfyNode):
"""
Frontend node that allows user to write their own options for a combo.
@ -260,6 +295,7 @@ class LogicExtension(ComfyExtension):
async def get_node_list(self) -> list[type[io.ComfyNode]]:
return [
SwitchNode,
OptionalSwitchNode,
CustomComboNode,
# SoftSwitchNode,
# ConvertStringToComboNode,

View File

@ -980,6 +980,10 @@ async def validate_inputs(prompt_id, prompt, item, validated):
input_filtered[x] = input_data_all[x]
if 'input_types' in validate_function_inputs:
input_filtered['input_types'] = [received_types]
for x in list(input_filtered.keys()):
if input_filtered[x] is io.EmptyInputSentinel:
del input_filtered[x]
ret = await _async_map_node_over_list(prompt_id, unique_id, obj_class, input_filtered, validate_function_name, v3_data=v3_data)
ret = await resolve_map_node_over_list_results(ret)