mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-07-07 15:10:50 +08:00
DynamicSlot: support required slots and always forceInput
- Add `optional` kwarg to DynamicSlot.Input (default True). When False, declaring a when=None Option is rejected because the unconnected branch is unreachable. - Always publish `forceInput=True` on the slot itself. slotType may include widget-capable types (INT/STRING/etc.) but a DynamicSlot is meant to look like a connection point, never a widget. Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
parent
346ee898cb
commit
8cac12afa8
@ -1279,7 +1279,7 @@ class DynamicSlot(ComfyTypeI):
|
||||
|
||||
class Input(DynamicInput):
|
||||
def __init__(self, id: str, options: list[DynamicSlot.Option],
|
||||
display_name: str=None, tooltip: str=None, lazy: bool=None, extra_dict=None):
|
||||
display_name: str=None, optional: bool=True, tooltip: str=None, lazy: bool=None, extra_dict=None):
|
||||
if not options:
|
||||
raise ValueError("DynamicSlot.Input: at least one Option is required")
|
||||
for opt in options:
|
||||
@ -1287,7 +1287,7 @@ class DynamicSlot(ComfyTypeI):
|
||||
raise ValueError(
|
||||
f"DynamicSlot.Input: options must be DynamicSlot.Option instances, got {opt!r}"
|
||||
)
|
||||
super().__init__(id, display_name, True, tooltip, lazy, extra_dict)
|
||||
super().__init__(id, display_name, optional, tooltip, lazy, extra_dict)
|
||||
self.options = options
|
||||
# Enforce uniqueness: each io_type (and the unconnected case) may
|
||||
# appear in at most one option's ``when``. Also derive the slot's
|
||||
@ -1315,6 +1315,12 @@ class DynamicSlot(ComfyTypeI):
|
||||
"DynamicSlot.Input: at least one Option must have a non-None `when`; "
|
||||
"a slot with only a `when=None` option can never be connected"
|
||||
)
|
||||
# A required slot demands a link, so the when=None branch is unreachable.
|
||||
if not optional and seen_none:
|
||||
raise ValueError(
|
||||
"DynamicSlot.Input: optional=False forbids when=None options; "
|
||||
"the unconnected branch is unreachable when a link is required"
|
||||
)
|
||||
self._slot_io_type = ",".join(connected_types)
|
||||
|
||||
# parse_class_inputs dispatches on the class io_type (COMFY_DYNAMICSLOT_V3),
|
||||
@ -1335,6 +1341,9 @@ class DynamicSlot(ComfyTypeI):
|
||||
return super().as_dict() | prune_dict({
|
||||
"slotType": self._slot_io_type,
|
||||
"options": [o.as_dict() for o in self.options],
|
||||
# Always render as a connector — slotType may include widget-capable
|
||||
# types (INT/STRING/etc.) but a DynamicSlot is a connection point.
|
||||
"forceInput": True,
|
||||
})
|
||||
|
||||
def validate(self):
|
||||
|
||||
@ -147,6 +147,30 @@ def test_input_rejects_non_option_entry():
|
||||
io.DynamicSlot.Input("x", options=[_opt(io.Image, ["a"]), "not an option"])
|
||||
|
||||
|
||||
def test_input_defaults_to_optional_and_always_force_input():
|
||||
"""The slot is always rendered as a connector, never as a widget, even
|
||||
when slotType includes widget-capable types like INT/STRING."""
|
||||
inp = io.DynamicSlot.Input("x", options=[_opt(io.Int, ["n"])])
|
||||
d = inp.as_dict()
|
||||
assert d["forceInput"] is True
|
||||
# default optional=True → slot lives in optional bucket via DynamicInput
|
||||
assert inp.optional is True
|
||||
|
||||
|
||||
def test_input_required_slot_allowed_without_when_none():
|
||||
inp = io.DynamicSlot.Input("x", optional=False, options=[_opt(io.Image, ["a"])])
|
||||
assert inp.optional is False
|
||||
|
||||
|
||||
def test_input_required_slot_rejects_when_none_option():
|
||||
with pytest.raises(ValueError, match="optional=False forbids when=None"):
|
||||
io.DynamicSlot.Input(
|
||||
"x",
|
||||
optional=False,
|
||||
options=[_opt(io.Image, ["a"]), _opt(None, ["b"])],
|
||||
)
|
||||
|
||||
|
||||
def test_input_get_all_prepends_self_and_dedups_children():
|
||||
inp = io.DynamicSlot.Input("x", options=[
|
||||
_opt(io.Image, ["shared", "image_only"]),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user