Skip to content

Commit 222f3f8

Browse files
authored
Merge pull request #840 from LalatenduMohanty/reduce_graph_refactor
refactor(graph): extract helpers from reduce_graph
2 parents a098d3c + f7175d2 commit 222f3f8

File tree

2 files changed

+361
-48
lines changed

2 files changed

+361
-48
lines changed

src/fromager/commands/graph.py

Lines changed: 91 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,91 @@ def to_dot(
106106
)
107107

108108

109+
def _get_nodes_for_reduction(
110+
graph: DependencyGraph,
111+
install_only: bool,
112+
) -> list[DependencyNode]:
113+
"""Determine starting node set based on install_only flag."""
114+
if install_only:
115+
nodes: list[DependencyNode] = [graph.nodes[ROOT]]
116+
nodes.extend(graph.get_install_dependencies())
117+
return nodes
118+
return list(graph.get_all_nodes())
119+
120+
121+
def _find_customized_nodes(
122+
wkctx: context.WorkContext,
123+
nodes: list[DependencyNode],
124+
) -> list[DependencyNode]:
125+
"""Filter nodes to find only those with customizations."""
126+
customized_nodes: list[DependencyNode] = []
127+
for node in nodes:
128+
pbi = wkctx.settings.package_build_info(node.canonicalized_name)
129+
if node.canonicalized_name != ROOT and pbi.has_customizations:
130+
customized_nodes.append(node)
131+
return customized_nodes
132+
133+
134+
def _find_customized_dependencies_for_node(
135+
wkctx: context.WorkContext,
136+
node: DependencyNode,
137+
install_only: bool,
138+
) -> dict[str, str]:
139+
"""
140+
Find all reachable customized nodes from a given node using depth-first search.
141+
142+
Returns:
143+
Dictionary mapping child keys to their requirement strings.
144+
Format: {child_key: requirement_string}
145+
"""
146+
dependencies: dict[str, str] = {}
147+
visited: set[str] = set()
148+
# Stack contains: (current_node, path_from_start, original_requirement)
149+
stack: list[tuple[DependencyNode, list[str], str | None]] = [(node, [], None)]
150+
151+
while stack:
152+
current_node, path, original_req = stack.pop()
153+
154+
if current_node.key in visited:
155+
continue
156+
visited.add(current_node.key)
157+
158+
for edge in current_node.children:
159+
# Skip build dependencies if install_only is True
160+
if install_only and edge.req_type.is_build_requirement:
161+
continue
162+
163+
child = edge.destination_node
164+
child_pbi = wkctx.settings.package_build_info(child.canonicalized_name)
165+
new_path = path + [current_node.key]
166+
167+
# Use the first requirement we encounter in the path
168+
current_req = original_req if original_req else str(edge.req)
169+
170+
# If the child has customizations, add it as a direct dependency
171+
if child_pbi.has_customizations:
172+
dependencies[child.key] = current_req
173+
else:
174+
# If the child doesn't have customizations, continue traversing
175+
stack.append((child, new_path, current_req))
176+
177+
return dependencies
178+
179+
180+
def _build_reduced_dependency_map(
181+
wkctx: context.WorkContext,
182+
customized_nodes: list[DependencyNode],
183+
install_only: bool,
184+
) -> dict[str, dict[str, str]]:
185+
"""Build dependency map for all customized nodes."""
186+
reduced_dependencies: dict[str, dict[str, str]] = {}
187+
for node in customized_nodes:
188+
reduced_dependencies[node.key] = _find_customized_dependencies_for_node(
189+
wkctx, node, install_only
190+
)
191+
return reduced_dependencies
192+
193+
109194
def reduce_graph(
110195
wkctx: context.WorkContext,
111196
graph: DependencyGraph,
@@ -120,57 +205,16 @@ def reduce_graph(
120205
- Dictionary mapping each included node to its direct dependencies with requirement info
121206
Format: {parent_key: {child_key: requirement_string}}
122207
"""
123-
# Start with all nodes or just install dependencies
124-
if install_only:
125-
all_nodes: list[DependencyNode] = [graph.nodes[ROOT]]
126-
all_nodes.extend(graph.get_install_dependencies())
127-
else:
128-
all_nodes = list(graph.get_all_nodes())
208+
# Get starting node set based on install_only flag
209+
all_nodes = _get_nodes_for_reduction(graph, install_only)
129210

130211
# Find nodes with customizations
131-
customized_nodes: list[DependencyNode] = []
132-
for node in all_nodes:
133-
pbi = wkctx.settings.package_build_info(node.canonicalized_name)
134-
if node.canonicalized_name != ROOT and pbi.has_customizations:
135-
customized_nodes.append(node)
212+
customized_nodes = _find_customized_nodes(wkctx, all_nodes)
136213

137214
# Build reduced dependency relationships with requirement tracking
138-
reduced_dependencies: dict[str, dict[str, str]] = {}
139-
140-
for node in customized_nodes:
141-
reduced_dependencies[node.key] = {}
142-
143-
# Find all reachable customized nodes from this node
144-
visited: set[str] = set()
145-
# Stack now includes: (current_node, path_from_start, original_requirement)
146-
stack: list[tuple[DependencyNode, list[str], str | None]] = [(node, [], None)]
147-
148-
while stack:
149-
current_node, path, original_req = stack.pop()
150-
151-
if current_node.key in visited:
152-
continue
153-
visited.add(current_node.key)
154-
155-
for edge in current_node.children:
156-
# Skip build dependencies if install_only is True
157-
if install_only and edge.req_type.is_build_requirement:
158-
continue
159-
160-
child = edge.destination_node
161-
child_pbi = wkctx.settings.package_build_info(child.canonicalized_name)
162-
new_path = path + [current_node.key]
163-
164-
# Use the first requirement we encounter in the path
165-
current_req = original_req if original_req else str(edge.req)
166-
167-
# If the child has customizations, add it as a direct dependency
168-
if child_pbi.has_customizations:
169-
# Store the requirement string for this dependency
170-
reduced_dependencies[node.key][child.key] = current_req
171-
else:
172-
# If the child doesn't have customizations, continue traversing
173-
stack.append((child, new_path, current_req))
215+
reduced_dependencies = _build_reduced_dependency_map(
216+
wkctx, customized_nodes, install_only
217+
)
174218

175219
return customized_nodes, reduced_dependencies
176220

0 commit comments

Comments
 (0)