App: Remove duplicate from dependency graph #77
1 changed files with 19 additions and 21 deletions
App.__find_circular_deps(): Fix cycle path
__find_circular_deps_recursive() builds the cycle path by inserting each visited dependency at the front of the list on the way back up, and __find_circular_deps() appends the project where the cycle was detected at the end. The path therefore starts at the first child of the DFS root instead of at the project that closes the cycle, and the closing project appears twice: for a dependency cycle between projects A and B, find_circular_deps() reports "B -> A -> A" instead of "A -> B -> A". Keep the DFS stack as an ordered list, and when a dependency is already on the stack, return the part of the stack from that dependency to the top, plus the dependency itself, which starts and ends at the same project. Reverse the result before returning, because an edge a -> b in the flipped graph means that b depends on a, so the cycle is reported in the original dependency direction. Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2 Signed-off-by: Jan Lindemann <jan@janware.com>
commit
bd7cfa4ff8
|
|
@ -258,45 +258,43 @@ class App(Base):
|
||||||
project: str,
|
project: str,
|
||||||
graph: Graph,
|
graph: Graph,
|
||||||
unvisited: list[str],
|
unvisited: list[str],
|
||||||
temp: set[str],
|
stack: list[str],
|
||||||
path: list[str],
|
) -> list[str] | None:
|
||||||
) -> str | None:
|
if project in stack:
|
||||||
if project in temp:
|
|
||||||
log(DEBUG, 'found circular dependency at project', project)
|
log(DEBUG, 'found circular dependency at project', project)
|
||||||
return project
|
idx = stack.index(project)
|
||||||
|
return stack[idx:] + [project]
|
||||||
if project not in unvisited:
|
if project not in unvisited:
|
||||||
return None
|
return None
|
||||||
temp.add(project)
|
stack.append(project)
|
||||||
if project in graph:
|
if project in graph:
|
||||||
for dep in graph[project]:
|
for dep in graph[project]:
|
||||||
last = self.__find_circular_deps_recursive(
|
cycle = self.__find_circular_deps_recursive(
|
||||||
dep, graph, unvisited, temp, path
|
dep, graph, unvisited, stack
|
||||||
)
|
)
|
||||||
if last is not None:
|
if cycle is not None:
|
||||||
path.insert(0, dep)
|
return cycle
|
||||||
return last
|
|
||||||
unvisited.remove(project)
|
unvisited.remove(project)
|
||||||
temp.remove(project)
|
stack.pop()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __find_circular_deps(self, projects: list[str],
|
def __find_circular_deps(self, projects: list[str],
|
||||||
flavours: list[str]) -> list[str]:
|
flavours: list[str]) -> list[str]:
|
||||||
graph: Graph = {}
|
graph: Graph = {}
|
||||||
ret: list[str] = []
|
|
||||||
self.__read_dep_graph(projects, flavours, graph)
|
self.__read_dep_graph(projects, flavours, graph)
|
||||||
unvisited = list(graph.keys())
|
unvisited = list(graph.keys())
|
||||||
temp: set[str] = set()
|
|
||||||
flipped = self.__flip_dep_graph(graph)
|
flipped = self.__flip_dep_graph(graph)
|
||||||
while unvisited:
|
while unvisited:
|
||||||
project = unvisited[0]
|
project = unvisited[0]
|
||||||
log(DEBUG, 'Checking circular dependency of', project)
|
log(DEBUG, 'Checking circular dependency of', project)
|
||||||
last = self.__find_circular_deps_recursive(
|
cycle = self.__find_circular_deps_recursive(project, flipped, unvisited, [])
|
||||||
project, flipped, unvisited, temp, ret
|
if cycle is not None:
|
||||||
)
|
# An edge a -> b in the flipped graph means that b
|
||||||
if last is not None:
|
# depends on a, so reverse to report the cycle in the
|
||||||
log(DEBUG, f'Found circular dependency below {project}, last is {last}')
|
# original direction
|
||||||
ret.append(last)
|
cycle = list(reversed(cycle))
|
||||||
return ret
|
log(DEBUG, f'Found circular dependency: {" -> ".join(cycle)}')
|
||||||
|
return cycle
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def __init__(self, distro: Distro | None = None) -> None:
|
def __init__(self, distro: Distro | None = None) -> None:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue