diff --git a/comfy_execution/caching.py b/comfy_execution/caching.py index 3a04f8651..6c99274a5 100644 --- a/comfy_execution/caching.py +++ b/comfy_execution/caching.py @@ -275,7 +275,8 @@ def to_hashable(obj, max_nodes=_MAX_SIGNATURE_CONTAINER_VISITS): snapshots = {} sort_memo = {} processed = 0 - stack = [(obj, False)] + # Keep traversal state separate from container snapshots/results. + work_stack = [(obj, False)] def resolve_value(value): """Resolve a child value from the completed memo table when available.""" @@ -309,8 +310,8 @@ def to_hashable(obj, max_nodes=_MAX_SIGNATURE_CONTAINER_VISITS): return (container_tag, tuple(value for _, value in ordered_items)) - while stack: - current, expanded = stack.pop() + while work_stack: + current, expanded = work_stack.pop() current_type = type(current) if current_type in _PRIMITIVE_SIGNATURE_TYPES or current_type is Unhashable: @@ -388,7 +389,7 @@ def to_hashable(obj, max_nodes=_MAX_SIGNATURE_CONTAINER_VISITS): return Unhashable() active.add(current_id) - stack.append((current, True)) + work_stack.append((current, True)) if current_type is dict: try: items = list(current.items()) @@ -398,8 +399,8 @@ def to_hashable(obj, max_nodes=_MAX_SIGNATURE_CONTAINER_VISITS): active.discard(current_id) continue for key, value in reversed(items): - stack.append((value, False)) - stack.append((key, False)) + work_stack.append((value, False)) + work_stack.append((key, False)) else: try: items = list(current) @@ -409,7 +410,7 @@ def to_hashable(obj, max_nodes=_MAX_SIGNATURE_CONTAINER_VISITS): active.discard(current_id) continue for item in reversed(items): - stack.append((item, False)) + work_stack.append((item, False)) return memo.get(id(obj), Unhashable()) diff --git a/tests/execution/test_caching.py b/tests/execution/test_caching.py index 192c3102e..a92a8a416 100644 --- a/tests/execution/test_caching.py +++ b/tests/execution/test_caching.py @@ -49,6 +49,21 @@ def test_get_immediate_node_signature_canonicalizes_non_link_inputs(monkeypatch) ) +def test_to_hashable_walks_dicts_without_rebinding_traversal_stack(): + live_value = { + "outer": {"nested": [2, 3]}, + "items": [{"leaf": 4}], + } + + assert caching.to_hashable(live_value) == ( + "dict", + ( + ("items", ("list", (("dict", (("leaf", 4),)),))), + ("outer", ("dict", (("nested", ("list", (2, 3))),))), + ), + ) + + def test_get_immediate_node_signature_fails_closed_for_opaque_non_link_input(monkeypatch): class OpaqueRuntimeValue: pass