Source code for simvx.editor.error_recovery
"""Error handling for scene instantiation and hot reload."""
import logging
import traceback
from simvx.core import Node
from simvx.core.descriptors import Signal
log = logging.getLogger(__name__)
[docs]
class ErrorRecovery:
"""Manages error state for nodes that fail during instantiation or reload.
When a class __init__ raises, the node is marked with _script_error = True.
The editor shows a red indicator and the traceback in the output panel.
When the file is fixed and hot-reloaded, the node is re-instantiated.
"""
def __init__(self):
self.error_occurred = Signal() # emits (node, traceback_str)
self.error_cleared = Signal() # emits (node,)
self._errored_nodes: dict[str, tuple[Node, str]] = {} # node_path -> (node, traceback)
[docs]
def mark_errored(self, node: Node, tb: str):
"""Mark a node as errored with traceback info."""
node._script_error = True
path = _node_path(node)
self._errored_nodes[path] = (node, tb)
self.error_occurred.emit(node, tb)
log.error("Node %s errored:\n%s", node.name, tb)
[docs]
def clear_error(self, node: Node):
"""Clear error state on a node (after successful reload)."""
node._script_error = False
path = _node_path(node)
self._errored_nodes.pop(path, None)
self.error_cleared.emit(node)
[docs]
@property
def errored_nodes(self) -> list[tuple[Node, str]]:
"""All errored nodes with their tracebacks."""
return list(self._errored_nodes.values())
[docs]
def try_reinstantiate(self, node: Node, cls: type) -> Node | None:
"""Try to re-instantiate an errored node with a (potentially fixed) class.
Returns the new node on success, or None if the class still raises.
"""
try:
new_node = cls()
self.clear_error(node)
return new_node
except Exception:
tb = traceback.format_exc()
self.mark_errored(node, tb)
return None
def _node_path(node: Node) -> str:
"""Get a stable path string for a node."""
parts = []
n = node
while n:
parts.append(n.name)
n = n.parent
return "/".join(reversed(parts))