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:
Jedrzej Kosinski
2026-06-01 19:28:15 -07:00
parent 346ee898cb
commit 8cac12afa8
2 changed files with 35 additions and 2 deletions

View File

@@ -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"]),