better warnings

This commit is contained in:
Nicolas Predella 2026-10-09 17:03:55 +03:00
parent 973e38fe53
commit 38acdafa55
2 changed files with 105 additions and 6 deletions

View file

@ -126,13 +126,13 @@ class SliceNode(Node, AutoBlIdName):
from ..debug import dump_node_execution
return dump_node_execution(self, socket_name, output_val=None, duration_s=0.0)
def resolve(self, fieldname) -> Any:
def resolve(self, fieldname: str) -> Any:
cache = getattr(self, "_current_cache", None)
visited = getattr(self, "_current_visited", None)
inputs = getattr(self, "inputs", None)
if inputs is None:
return None
return resolve_input(inputs.get(fieldname), cache=cache, visited=visited)
return resolve_input(None, cache=cache, visited=visited, fieldname=fieldname, node=self)
return resolve_input(inputs.get(fieldname), cache=cache, visited=visited, fieldname=fieldname, node=self)
def add_output(self, bl_idname: str, name: str = "", identifier: str = "", hide: bool = False, display_shape: str = "", description: str = ""):
bl_idname = NATIVE_SOCKET_MAP.get(bl_idname, bl_idname)
@ -175,12 +175,12 @@ class SliceNode(Node, AutoBlIdName):
}
self._update_multi_groups()
def resolve_multi_input(self, fieldname) -> Any:
def resolve_multi_input(self, fieldname: str) -> Any:
cache = getattr(self, "_current_cache", None)
visited = getattr(self, "_current_visited", None)
# Search by name or identifier prefix for multi-groups
inputs = [i for i in list(self.inputs) if i.name == fieldname or i.identifier.startswith(f"{fieldname}_ms_in_")]
return [resolve_input(r, cache=cache, visited=visited) for r in inputs]
return [resolve_input(r, cache=cache, visited=visited, fieldname=fieldname, node=self) for r in inputs]
def _update_multi_groups(self):
if not self.get('multi_groups'): return
@ -582,14 +582,80 @@ def _follow_reroute(current: NodeSocket) -> NodeSocket:
return current
def _format_node_description(node: Any) -> str:
if node is None:
return ""
name = getattr(node, "name", "")
bl_label = getattr(node, "bl_label", "")
bl_idname = getattr(node, "bl_idname", "") or type(node).__name__
label = bl_label or bl_idname
if name and label and name != label:
return f"'{name}' ({label})"
elif name:
return f"'{name}'"
elif label:
return f"'{label}'"
return str(node)
def _extract_caller_context() -> tuple[Any, str | None]:
try:
import sys
for depth in range(2, 6):
try:
frame = sys._getframe(depth)
except ValueError:
break
caller_self = frame.f_locals.get("self")
caller_node = None
if caller_self is not None and (
isinstance(caller_self, Node) or
hasattr(caller_self, "bl_idname") or
hasattr(caller_self, "inputs")
):
caller_node = caller_self
caller_fieldname = (
frame.f_locals.get("fieldname") or
frame.f_locals.get("socket_name") or
frame.f_locals.get("name")
)
if not isinstance(caller_fieldname, str):
caller_fieldname = None
if caller_node is not None or caller_fieldname is not None:
return caller_node, caller_fieldname
except Exception:
pass
return None, None
def resolve_input(
socket: SliceNodeSocket | None,
*,
cache: _EvalCache | None = None,
visited: _VisitedSet | None = None,
fieldname: str | None = None,
node: Any = None,
):
if socket is None:
raise Exception(f"The queried socket is non-existant")
if node is None or fieldname is None:
caller_node, caller_fieldname = _extract_caller_context()
if node is None:
node = caller_node
if fieldname is None:
fieldname = caller_fieldname
node_str = _format_node_description(node)
if fieldname and node_str:
raise NodeException(f"The queried socket '{fieldname}' on node {node_str} is non-existent")
elif fieldname:
raise NodeException(f"The queried socket '{fieldname}' is non-existent")
elif node_str:
raise NodeException(f"The queried socket on node {node_str} is non-existent")
else:
raise NodeException("The queried socket is non-existent")
assert socket.node

View file

@ -321,7 +321,40 @@ class TestNodeDebugSystem(unittest.TestCase):
dump = node.dump_inputs("Val")
self.assertEqual(dump["inputs"]["Val"]["value_summary"], 42.0)
def test_non_existent_socket_error_message_with_resolve(self):
from NodeSlicer.infra.blender.blender_slice_nodes_generic import NodeException
class TestNode(SliceNode):
bl_idname = "SLICENODE_Test"
bl_label = "Test Node"
def __init__(self):
self.name = "My Test Node"
self.inputs = DummySocketCollection([
DummyNodeSocket("Valid", "Valid", "NodeSocketFloat", value=1.0)
], node=self)
node = TestNode()
with self.assertRaises(NodeException) as ctx:
node.resolve("NonExistentSocket")
err_msg = str(ctx.exception)
self.assertIn("NonExistentSocket", err_msg)
self.assertIn("My Test Node", err_msg)
self.assertIn("Test Node", err_msg)
self.assertIn("non-existent", err_msg)
def test_non_existent_socket_error_message_isolated(self):
from NodeSlicer.infra.blender.blender_slice_nodes_generic import NodeException, resolve_input
with self.assertRaises(NodeException) as ctx:
resolve_input(None)
err_msg = str(ctx.exception)
self.assertIn("non-existent", err_msg)
if __name__ == '__main__':
unittest.main()