deepset-ai / deepset-ai/haystack
_merge_super_component_pipelines docstring contradicts the function signature and implementation
@anakin87 is already working on this.
Since Sep 8, 2026.
- Dominant language
- Python
- Stars
- 26.6k
- Forks
- 3.2k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 194
Description
Bug: _merge_super_component_pipelines docstring contradicts the function signature and implementation
Haystack version: 3.1.0rc0 (upstream 01b43d4ee)
Affected code: haystack/core/pipeline/base.py:1679-1693 (docstring for _merge_super_component_pipelines)
Problem
The docstring for _merge_super_component_pipelines claims the function returns a tuple of three values, but the function signature is tuple[MultiDiGraph, dict[str, str]] and the implementation returns only two values. The 2nd return value is also described incorrectly (says "boolean" but the actual type is dict[str, str]). There's also a typo: "all it's" should be "all its".
# haystack/core/pipeline/base.py:1679-1693
def _merge_super_component_pipelines(self) -> tuple[networkx.MultiDiGraph, dict[str, str]]:
"""
Merge the internal pipelines of SuperComponents into the main pipeline graph structure.
This creates a new networkx.MultiDiGraph containing all the components from both the main pipeline
and all the internal SuperComponents' pipelines. The SuperComponents are removed and their internal
components are connected to corresponding input and output sockets of the main pipeline.
:returns:
A tuple containing:
- A networkx.MultiDiGraph with the expanded structure of the main pipeline and all it's SuperComponents
- A dictionary mapping component names to boolean indicating that this component was part of a
SuperComponent
- A dictionary mapping component names to their SuperComponent name
"""
merged_graph = self.graph.copy()
super_component_mapping: dict[str, str] = {}
for super_name, super_component in self._find_super_components():
internal_pipeline = super_component.pipeline # type: ignore
internal_graph = internal_pipeline.graph.copy()
# Mark all components in the internal pipeline as being part of a SuperComponent
for node in internal_graph.nodes():
super_component_mapping[node] = super_name
...
return merged_graph # noqa: RET504 (function only returns one value, but the docstring promises three)
The signature is tuple[MultiDiGraph, dict[str, str]] (two values). The caller unpacks two values: graph, super_component_mapping = self._merge_super_component_pipelines(). The function body returns only one value, merged_graph (the second return is implicit None).
The actual super_component_mapping is a dict[str, str] mapping component names to their SuperComponent's name (not a boolean). The docstring describes the second dict as mapping to a boolean (which would be a different schema entirely), and invents a non-existent third dict that the function never returns.
This is the same class of bug as #12636 (docstring references names the code does not have) and as the #12643 I just filed for breakpoint.py (comment contradicts the code).
Why this matters
- Future-maintainer risk. A contributor who trusts the docstring might add code that uses the (non-existent) third return value as a boolean flag, or rename the existing
dict[str, str]to match the docstring's "boolean" description, breaking callers. The current call site at line 916 (graph, super_component_mapping = self._merge_super_component_pipelines()) relies on the real shape, not the documented shape. - Editor / IDE risk. A type checker or IDE that uses the docstring would flag the
unpack(a, b, c)site as incorrect, and the actual two-value unpack as a bug, based on the false promise of three returns. - Real failure mode on read. A user reading the docstring would expect
super_component_mappingto be a boolean flag dict (e.g.,{"comp1": True, "comp2": False}), then try to writeif super_component_mapping[comp]:and get aTypeErrorbecause the actual values are strings.
Reproducer (verified locally against upstream/main 01b43d4ee)
import inspect
from haystack.core.pipeline.base import PipelineBase
sig = inspect.signature(PipelineBase._merge_super_component_pipelines)
print(sig.return_annotation)
# tuple[networkx.MultiDiGraph, dict[str, str]] — TWO values
doc = PipelineBase._merge_super_component_pipelines.__doc__
# Counts the bullets under ":returns:":
n_bullets = sum(1 for line in doc.splitlines() if line.lstrip().startswith("- "))
print(n_bullets)
# 3 — but the function only returns 2 values
The signature says two returns, the docstring says three.
Proposed fix
Replace the :returns: block with a description that matches the actual signature and behavior. There is no bool flag dict in the implementation; the only return values are the merged graph and a dict[str, str] mapping component names to their SuperComponent name:
:returns:
- A tuple containing:
- - A networkx.MultiDiGraph with the expanded structure of the main pipeline and all it's SuperComponents
- - A dictionary mapping component names to boolean indicating that this component was part of a
- SuperComponent
- - A dictionary mapping component names to their SuperComponent name
+ A tuple containing:
+ - A networkx.MultiDiGraph with the expanded structure of the main pipeline and all its SuperComponents
+ - A dict[str, str] mapping each component name to the name of the SuperComponent that contains it
The merged_graph is the new top-level graph; the super_component_mapping is the only other return value. There is no third return value.
Suggested regression test
def test_merge_super_component_pipelines_docstring_matches_signature(self):
"""The docstring for `_merge_super_component_pipelines` must not promise return values the function does not have.
A docstring that lists three return values where the signature returns two invites a future
contributor to add a call site that does `a, b, c = pipeline._merge_super_component_pipelines()` and
silently break at runtime. The test pins the docstring bullets to the signature length.
"""
import inspect
from haystack.core.pipeline.base import PipelineBase
sig = inspect.signature(PipelineBase._merge_super_component_pipelines)
doc = PipelineBase._merge_super_component_pipelines.__doc__ or ""
n_bullets = sum(1 for line in doc.splitlines() if line.lstrip().startswith("- "))
assert n_bullets == 2, f"Docstring has {n_bullets} bullets under :returns: but signature returns 2 values."
assert "all it's" not in doc, "Typo: 'all it's' should be 'all its'."
Scope
One file. Pure documentation hygiene: 5-line docstring edit + an optional regression test. No public API change, no behavioral change.
Alternatives considered
- Fix the code to match the docstring (add a third return value, change
dict[str, str]todict[str, bool]). Out of scope: changing the return shape would break the existing call site atbase.py:916and any user code that uses the current shape. The docstring is the wrong side; the code is the right side. - Remove the
:returns:block entirely and let users inspect the signature. Sphinx / IDEs handle this gracefully, but a one-paragraph description is more discoverable than a bare signature. - Leave the docstring as is and just fix the typo. Misses the more important claim that the function returns three values. The three-vs-two mismatch is the more misleading part of the docstring.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.