diff --git a/comfy_api/latest/_io.py b/comfy_api/latest/_io.py index a7228acac..d6e92c17b 100644 --- a/comfy_api/latest/_io.py +++ b/comfy_api/latest/_io.py @@ -1369,36 +1369,7 @@ _DynamicInputFunc = Callable[ ] DYNAMIC_INPUT_LOOKUP: dict[str, _DynamicInputFunc] = {} def register_dynamic_input_func(io_type: str, func: _DynamicInputFunc): - """Register a dynamic-input expansion callback. - - Accepts both the current 6-argument form and the legacy 5-argument form - (without ``live_input_types``). Legacy callables are silently wrapped so - custom nodes that registered against the older signature continue to work. - """ - try: - sig = inspect.signature(func) - param_count = sum( - 1 for p in sig.parameters.values() - if p.kind in (inspect.Parameter.POSITIONAL_ONLY, - inspect.Parameter.POSITIONAL_OR_KEYWORD, - inspect.Parameter.VAR_POSITIONAL) - ) - # 5 = legacy signature (no live_input_types). If the callable has - # *args we can't be certain, so treat it as new-style. - has_varargs = any(p.kind is inspect.Parameter.VAR_POSITIONAL for p in sig.parameters.values()) - is_legacy = (param_count == 5 and not has_varargs) - except (TypeError, ValueError): - # Builtins / C-implemented callables without introspectable signatures - # are assumed to be new-style. - is_legacy = False - - if is_legacy: - _legacy = func - def _adapter(out_dict, live_inputs, value, input_type, curr_prefix, live_input_types=None): - _legacy(out_dict, live_inputs, value, input_type, curr_prefix) - DYNAMIC_INPUT_LOOKUP[io_type] = _adapter - else: - DYNAMIC_INPUT_LOOKUP[io_type] = func + DYNAMIC_INPUT_LOOKUP[io_type] = func def get_dynamic_input_func(io_type: str) -> _DynamicInputFunc: return DYNAMIC_INPUT_LOOKUP[io_type] diff --git a/tests-unit/comfy_api_test/test_register_dynamic_input_func.py b/tests-unit/comfy_api_test/test_register_dynamic_input_func.py deleted file mode 100644 index afabf9fe7..000000000 --- a/tests-unit/comfy_api_test/test_register_dynamic_input_func.py +++ /dev/null @@ -1,79 +0,0 @@ -"""Backward-compat tests for ``register_dynamic_input_func``. - -When ``live_input_types`` was added as a sixth argument to the dynamic-input -expansion callback, third-party custom nodes that registered against the -original 5-argument signature would otherwise crash with ``TypeError`` the -first time their input was expanded. ``register_dynamic_input_func`` wraps -such legacy callables transparently. -""" - -from __future__ import annotations - -from comfy_api.latest import _io - - -def test_legacy_5arg_callback_is_wrapped_transparently(): - received = {} - - def legacy(out_dict, live_inputs, value, input_type, curr_prefix): - received["args"] = (out_dict, live_inputs, value, input_type, curr_prefix) - - io_type = "TEST_LEGACY_5ARG_V3" - try: - _io.register_dynamic_input_func(io_type, legacy) - fn = _io.get_dynamic_input_func(io_type) - - # Caller invokes with 6 arguments (current signature). The shim must - # strip the trailing live_input_types argument before delegating. - fn({"required": {}}, {"a": 1}, ("X", {}), "required", ["p"], {"a": "INT"}) - - assert received["args"] == ( - {"required": {}}, {"a": 1}, ("X", {}), "required", ["p"] - ) - finally: - _io.DYNAMIC_INPUT_LOOKUP.pop(io_type, None) - - -def test_new_6arg_callback_passes_live_input_types_through(): - received = {} - - def modern(out_dict, live_inputs, value, input_type, curr_prefix, live_input_types=None): - received["live_input_types"] = live_input_types - - io_type = "TEST_MODERN_6ARG_V3" - try: - _io.register_dynamic_input_func(io_type, modern) - fn = _io.get_dynamic_input_func(io_type) - fn({}, {}, ("X", {}), "required", None, {"foo": "IMAGE"}) - assert received["live_input_types"] == {"foo": "IMAGE"} - finally: - _io.DYNAMIC_INPUT_LOOKUP.pop(io_type, None) - - -def test_callable_with_uninspectable_signature_assumed_modern(): - """``functools.partial`` and C builtins may have no introspectable signature. - - The shim must not blow up; falling back to the new signature is the safe - choice (we get a clean TypeError if the callable really is too old, which - is no worse than the pre-shim behavior). - """ - calls = [] - - class _CallableObj: - # Lambdas / objects with __call__ are introspectable; partial with - # opaque builtins are not. Simulate the latter by raising in __signature__. - def __call__(self, *args, **kwargs): - calls.append((args, kwargs)) - - @property - def __signature__(self): - raise ValueError("uninspectable") - - io_type = "TEST_UNINSPECTABLE_V3" - try: - _io.register_dynamic_input_func(io_type, _CallableObj()) - fn = _io.get_dynamic_input_func(io_type) - fn({}, {}, ("X", {}), "required", None, {"x": "INT"}) - assert calls and len(calls[0][0]) == 6 - finally: - _io.DYNAMIC_INPUT_LOOKUP.pop(io_type, None)