From 0e4a15b7fb5ce5b979f5a51c412ca1842673ac3a Mon Sep 17 00:00:00 2001 From: Jedrzej Kosinski Date: Mon, 1 Jun 2026 17:08:50 -0700 Subject: [PATCH] TypeResolver: import internal _io so isinstance chain sees DynamicSlot The public `comfy_api.latest.io` re-export does not include DynamicSlot, so the isinstance chain in _effective_io_type raised AttributeError the first time it ran against any V3 input that wasn't an Autogrow. End-to-end test caught this with a MatchType chain feeding a probe node. Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501 Co-authored-by: Amp --- comfy_execution/type_resolver.py | 2 +- .../execution_test/test_type_resolver.py | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/comfy_execution/type_resolver.py b/comfy_execution/type_resolver.py index d22eee1ea..96df4fc2a 100644 --- a/comfy_execution/type_resolver.py +++ b/comfy_execution/type_resolver.py @@ -14,7 +14,7 @@ from __future__ import annotations import logging from typing import Any -from comfy_api.latest import io +from comfy_api.latest import _io as io from comfy_api.internal import _ComfyNodeInternal diff --git a/tests-unit/execution_test/test_type_resolver.py b/tests-unit/execution_test/test_type_resolver.py index e89874ca0..6d23223d0 100644 --- a/tests-unit/execution_test/test_type_resolver.py +++ b/tests-unit/execution_test/test_type_resolver.py @@ -328,6 +328,36 @@ def test_effective_slot_type_peels_autogrow(fake_nodes_module, TypeResolver): # compute_live_input_types # --------------------------------------------------------------------------- +def test_effective_slot_type_on_v3_plain_input(fake_nodes_module, TypeResolver): + """V3 input that is neither Autogrow/DynamicSlot/DynamicCombo must still resolve. + + Regression test: importing ``io`` from the public re-export skipped + ``DynamicSlot``, so an ``isinstance`` chain in ``_effective_io_type`` raised + ``AttributeError`` the first time it ran against a plain V3 input. + """ + from comfy_api.latest import io + + class BoolSink(io.ComfyNode): + @classmethod + def define_schema(cls): + return io.Schema( + node_id="BoolSink", + inputs=[io.Boolean.Input("flag")], + outputs=[io.Boolean.Output()], + ) + + @classmethod + def execute(cls, flag): + return io.NodeOutput(flag) + + BoolSink.GET_SCHEMA() + fake_nodes_module["BoolSink"] = BoolSink + prompt = {"n": {"class_type": "BoolSink", "inputs": {"flag": True}}} + r = TypeResolver(prompt) + assert r.get_declared_slot_io_type("n", "flag") == "BOOLEAN" + assert r.compute_live_input_types("n") == {"flag": "BOOLEAN"} + + def test_compute_live_input_types_mixes_links_and_literals(fake_nodes_module, TypeResolver): fake_nodes_module["Src"] = _v1_node(("MODEL",)) fake_nodes_module["Sink"] = _v1_node(