562:def _create_dataitem_callable(action: DataItem) -> Callable:
563-    def data_initializer(**kwargs):
564-        machine: StateChart = kwargs["machine"]
565-
566-        # Check for invoke param overrides — params from parent override child defaults
567-        invoke_params = getattr(machine, "_invoke_params", None)
568-        if invoke_params and action.id in invoke_params:
569-            setattr(machine.model, action.id, invoke_params[action.id])
570-            return
571-
572-        if action.expr:
573-            try:
574-                value = _eval(action.expr, **kwargs)
575-            except Exception:
576-                setattr(machine.model, action.id, None)
577-                raise
578-
579-        elif action.content:
580-            try:
581-                value = _eval(action.content, **kwargs)
582-            except Exception:
583-                value = action.content
584-        else:
585-            value = None
586-
587-        setattr(machine.model, action.id, value)
588-
589-    return data_initializer
590-
591-
592-def create_datamodel_action_callable(action: DataModel) -> "Callable | None":
593:    data_elements = [_create_dataitem_callable(item) for item in action.data]
594-    data_elements.extend([create_script_action_callable(script) for script in action.scripts])
595-
596-    if not data_elements:
597-        return None
598-
599-    initialized = False
600-
601-    def datamodel(*args, **kwargs):
602-        nonlocal initialized
603-        if initialized:
604-            return
605-        initialized = True
606-
31:class Listener:
32-    """Object reference that provides attributes to be used as callbacks.
33-
34-    Args:
35-        obj: Any object that will serve as lookup for attributes.
36-        skip_attrs: Protected attrs that will be ignored on the search.
37-    """
38-
39-    obj: object
40-    all_attrs: Set[str]
41-    resolver_id: str
42-
43-    @classmethod
44-    def from_obj(cls, obj, skip_attrs=None) -> "Listener":
45-        if isinstance(obj, Listener):
46-            return obj
47-        else:
48-            if skip_attrs is None:
49-                skip_attrs = set()
50-            all_attrs = set(dir(obj)) - skip_attrs
51-            return cls(obj, all_attrs, str(id(obj)))
52-
53-    def build_key(self, attr_name) -> str:
54-        return f"{attr_name}@{self.resolver_id}"
55-
56-
57-@dataclass
58:class Listeners:
59-    """Listeners that provides attributes to be used as callbacks."""
60-
61-    items: Tuple[Listener, ...]
62-    all_attrs: Set[str]
63-
64-    @classmethod
65-    def from_listeners(cls, listeners: Iterable["Listener"]) -> "Listeners":
66-        listeners = tuple(listeners)
67-        all_attrs = set().union(*(listener.all_attrs for listener in listeners))
68-        return cls(listeners, all_attrs)
69-
70-    def resolve(
71-        self,
72-        specs: "CallbackSpecList",
73-        registry: "CallbacksRegistry",
74-        allowed_references: SpecReference = SPECS_ALL,
75-    ):
76-        found_convention_specs = specs.conventional_specs & self.all_attrs
77-
78-        for spec in specs:
79-            if (spec.reference not in allowed_references) or (
80-                spec.is_convention and spec.func not in found_convention_specs
81-            ):
82-                continue
83-
84-            executor = registry[specs.grouper(spec.group).key]
85-            for key, builder in self.build(spec):
86-                executor.add(key, spec, builder)
87-
88-    def _take_callback(self, name: str, names_not_found_handler: Callable) -> Callable:
89-        callbacks: List[Callable] = []
90-        for key, builder in self.search_name(name):
