Skip to content

Commit cf53991

Browse files
committed
Implement a mapping of "id+prop" to callbacks that are outputs/inputs to those pairs
1 parent 79df12e commit cf53991

1 file changed

Lines changed: 33 additions & 5 deletions

File tree

dash/mcp/primitives/tools/callback_adapter_collection.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)