mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-03-23 18:13:28 +08:00
Harden to_hashable against cycles
This commit is contained in:
parent
4d9516b909
commit
880b51ac4f
@ -122,8 +122,14 @@ def _sanitize_signature_input(obj, depth=0, max_depth=32):
|
|||||||
# the foreign object and risk crashing on custom container semantics.
|
# the foreign object and risk crashing on custom container semantics.
|
||||||
return Unhashable()
|
return Unhashable()
|
||||||
|
|
||||||
def to_hashable(obj):
|
def to_hashable(obj, depth=0, max_depth=32, seen=None):
|
||||||
"""Convert prompt-safe built-in values into a stable hashable representation."""
|
"""Convert prompt-safe built-in values into a stable hashable representation."""
|
||||||
|
if depth >= max_depth:
|
||||||
|
return Unhashable()
|
||||||
|
|
||||||
|
if seen is None:
|
||||||
|
seen = set()
|
||||||
|
|
||||||
# Restrict recursion to plain built-in containers. Some custom nodes insert
|
# Restrict recursion to plain built-in containers. Some custom nodes insert
|
||||||
# runtime objects into prompt inputs for dynamic graph paths; walking those
|
# runtime objects into prompt inputs for dynamic graph paths; walking those
|
||||||
# objects as generic Mappings / Sequences is unsafe and can destabilize the
|
# objects as generic Mappings / Sequences is unsafe and can destabilize the
|
||||||
@ -131,16 +137,32 @@ def to_hashable(obj):
|
|||||||
obj_type = type(obj)
|
obj_type = type(obj)
|
||||||
if obj_type in (int, float, str, bool, bytes, type(None)):
|
if obj_type in (int, float, str, bool, bytes, type(None)):
|
||||||
return obj
|
return obj
|
||||||
elif obj_type is dict:
|
|
||||||
return ("dict", frozenset((to_hashable(k), to_hashable(v)) for k, v in obj.items()))
|
if obj_type in (dict, list, tuple, set, frozenset):
|
||||||
|
obj_id = id(obj)
|
||||||
|
if obj_id in seen:
|
||||||
|
return Unhashable()
|
||||||
|
seen = seen | {obj_id}
|
||||||
|
|
||||||
|
if obj_type is dict:
|
||||||
|
return (
|
||||||
|
"dict",
|
||||||
|
frozenset(
|
||||||
|
(
|
||||||
|
to_hashable(k, depth + 1, max_depth, seen),
|
||||||
|
to_hashable(v, depth + 1, max_depth, seen),
|
||||||
|
)
|
||||||
|
for k, v in obj.items()
|
||||||
|
),
|
||||||
|
)
|
||||||
elif obj_type is list:
|
elif obj_type is list:
|
||||||
return ("list", tuple(to_hashable(i) for i in obj))
|
return ("list", tuple(to_hashable(i, depth + 1, max_depth, seen) for i in obj))
|
||||||
elif obj_type is tuple:
|
elif obj_type is tuple:
|
||||||
return ("tuple", tuple(to_hashable(i) for i in obj))
|
return ("tuple", tuple(to_hashable(i, depth + 1, max_depth, seen) for i in obj))
|
||||||
elif obj_type is set:
|
elif obj_type is set:
|
||||||
return ("set", frozenset(to_hashable(i) for i in obj))
|
return ("set", frozenset(to_hashable(i, depth + 1, max_depth, seen) for i in obj))
|
||||||
elif obj_type is frozenset:
|
elif obj_type is frozenset:
|
||||||
return ("frozenset", frozenset(to_hashable(i) for i in obj))
|
return ("frozenset", frozenset(to_hashable(i, depth + 1, max_depth, seen) for i in obj))
|
||||||
else:
|
else:
|
||||||
return Unhashable()
|
return Unhashable()
|
||||||
|
|
||||||
|
|||||||
33
tests-unit/execution_test/caching_hashable_test.py
Normal file
33
tests-unit/execution_test/caching_hashable_test.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from comfy_execution.caching import Unhashable, to_hashable
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_hashable_returns_unhashable_for_cyclic_builtin_containers():
|
||||||
|
cyclic_list = []
|
||||||
|
cyclic_list.append(cyclic_list)
|
||||||
|
|
||||||
|
result = to_hashable(cyclic_list)
|
||||||
|
|
||||||
|
assert result[0] == "list"
|
||||||
|
assert len(result[1]) == 1
|
||||||
|
assert isinstance(result[1][0], Unhashable)
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_hashable_returns_unhashable_when_max_depth_is_reached():
|
||||||
|
nested = current = []
|
||||||
|
for _ in range(32):
|
||||||
|
next_item = []
|
||||||
|
current.append(next_item)
|
||||||
|
current = next_item
|
||||||
|
|
||||||
|
result = to_hashable(nested)
|
||||||
|
|
||||||
|
depth = 0
|
||||||
|
current = result
|
||||||
|
while isinstance(current, tuple):
|
||||||
|
assert current[0] == "list"
|
||||||
|
assert len(current[1]) == 1
|
||||||
|
current = current[1][0]
|
||||||
|
depth += 1
|
||||||
|
|
||||||
|
assert depth == 32
|
||||||
|
assert isinstance(current, Unhashable)
|
||||||
Loading…
Reference in New Issue
Block a user