Perform a topological sort on a dependency graph. Args: graph: A dependency graph represented as adjacency lists (node -> set of dependencies) Returns: A list of nodes in topological order (dependencies first)
(graph: Dict[str, Set[str]])
| 119 | |
| 120 | if next_node in new_graph[current]: |
| 121 | logger.debug(f"Breaking cycle by removing dependency: {current} -> {next_node}") |
| 122 | new_graph[current].remove(next_node) |
| 123 | break |
| 124 | |
| 125 | return new_graph |
| 126 | |
| 127 | |
| 128 | def topological_sort(graph: dict[str, set[str]]) -> list[str]: |
| 129 | """ |
| 130 | Perform a topological sort on a dependency graph. |
| 131 | |
| 132 | Args: |
| 133 | graph: A dependency graph represented as adjacency lists |
| 134 | (node -> set of dependencies) |
| 135 | |
| 136 | Returns: |
| 137 | A list of nodes in topological order (dependencies first) |
| 138 | """ |
| 139 | # First, check for and resolve cycles |
| 140 | acyclic_graph = resolve_cycles(graph) |
| 141 | |
| 142 | # Initialize in-degree counter for all nodes |
| 143 | in_degree = {node: 0 for node in acyclic_graph} |
| 144 | |
| 145 | # Count in-degrees |
| 146 | for node, dependencies in acyclic_graph.items(): |
| 147 | for dep in dependencies: |
| 148 | if dep in in_degree: |
| 149 | in_degree[dep] += 1 |
| 150 | |
| 151 | # Queue of nodes with no dependencies (in-degree of 0) |
| 152 | queue = deque([node for node, degree in in_degree.items() if degree == 0]) |
| 153 | |
| 154 | # Result list to store the topological order |
| 155 | result = [] |
| 156 | |
| 157 | # Process nodes in topological order |
| 158 | while queue: |
| 159 | node = queue.popleft() |
| 160 | result.append(node) |
| 161 | |
| 162 | # Reduce in-degree for each node that depends on the current node |
| 163 | for dependent, deps in acyclic_graph.items(): |
| 164 | if node in deps: |
| 165 | in_degree[dependent] -= 1 |
| 166 | if in_degree[dependent] == 0: |
| 167 | queue.append(dependent) |
| 168 | |
| 169 | # Check if the sort was successful (all nodes included) |
| 170 | if len(result) != len(acyclic_graph): |
| 171 | logger.warning("Topological sort failed: graph has cycles that weren't resolved") |
| 172 | # Return all nodes in some order to avoid breaking the process |
nothing calls this directly
no test coverage detected