@@ -86,8 +86,14 @@ def find_by_tool_name(self, name: str) -> CallbackAdapter | None:
8686 return cb
8787 return None
8888
89- def find_by_output (self , id_and_prop : str ) -> CallbackAdapter | None :
90- """Find the adapter that outputs to ``id_and_prop`` (``"component_id.property"``)."""
89+ @cached_property
90+ def outputs_by_prop (self ) -> dict [str , list [CallbackAdapter ]]:
91+ """Index ``id_and_prop`` → callbacks outputting it.
92+
93+ Mirrors the dash-renderer's ``outputMap`` (see
94+ ``dash-renderer/src/actions/dependencies.js``).
95+ """
96+ idx : dict [str , list [CallbackAdapter ]] = {}
9197 for cb in self ._callbacks :
9298 try :
9399 parsed = split_callback_id (cb .output_id )
@@ -96,9 +102,31 @@ def find_by_output(self, id_and_prop: str) -> CallbackAdapter | None:
96102 if isinstance (parsed , dict ):
97103 parsed = [parsed ]
98104 for p in parsed :
99- if f"{ p ['id' ]} .{ clean_property_name (p ['property' ])} " == id_and_prop :
100- return cb
101- return None
105+ key = f"{ p ['id' ]} .{ clean_property_name (p ['property' ])} "
106+ idx .setdefault (key , []).append (cb )
107+ return idx
108+
109+ @cached_property
110+ def inputs_by_prop (self ) -> dict [str , list [CallbackAdapter ]]:
111+ """Index ``id_and_prop`` → callbacks consuming it as input/state.
112+
113+ Mirrors the dash-renderer's ``inputMap`` (see
114+ ``dash-renderer/src/actions/dependencies.js``).
115+ Many callbacks may share a key.
116+ """
117+ idx : dict [str , list [CallbackAdapter ]] = {}
118+ for cb in self ._callbacks :
119+ # pylint: disable-next=protected-access
120+ deps = cb ._cb_info .get ("inputs" , []) + cb ._cb_info .get ("state" , [])
121+ for dep in deps :
122+ key = f"{ dep .get ('id' , 'unknown' )} .{ dep .get ('property' , 'unknown' )} "
123+ idx .setdefault (key , []).append (cb )
124+ return idx
125+
126+ def find_by_output (self , id_and_prop : str ) -> CallbackAdapter | None :
127+ """Find the adapter that outputs to ``id_and_prop`` (``"component_id.property"``)."""
128+ candidates = self .outputs_by_prop .get (id_and_prop , [])
129+ return candidates [0 ] if candidates else None
102130
103131 def get_initial_value (self , id_and_prop : str ) -> Any :
104132 """Return the initial value for ``id_and_prop`` (``"component_id.property"``).
0 commit comments