fix: resolve CI type checking and lint failures
This commit is contained in:
110
app/depnav/src/depnav/detector.py
Normal file
110
app/depnav/src/depnav/detector.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
"""Circular dependency detection utilities."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from .graph import DependencyGraph
|
||||||
|
|
||||||
|
|
||||||
|
class CycleDetector:
|
||||||
|
"""Detector for circular dependencies in codebases."""
|
||||||
|
|
||||||
|
def __init__(self, graph: DependencyGraph):
|
||||||
|
self.graph = graph
|
||||||
|
|
||||||
|
def detect_all_cycles(self) -> list[list[Path]]:
|
||||||
|
"""Detect all cycles in the dependency graph."""
|
||||||
|
return self.graph.detect_cycles()
|
||||||
|
|
||||||
|
def detect_cycles_for_file(self, file_path: Path) -> list[list[Path]]:
|
||||||
|
"""Detect cycles that include a specific file."""
|
||||||
|
all_cycles = self.detect_all_cycles()
|
||||||
|
return [
|
||||||
|
cycle for cycle in all_cycles if file_path in cycle
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_cyclic_files(self) -> list[Path]:
|
||||||
|
"""Get all files that are part of a cycle."""
|
||||||
|
cyclic_files = set()
|
||||||
|
for cycle in self.detect_all_cycles():
|
||||||
|
cyclic_files.update(cycle)
|
||||||
|
return list(cyclic_files)
|
||||||
|
|
||||||
|
def is_cyclic(self, file_path: Path) -> bool:
|
||||||
|
"""Check if a specific file is part of a cycle."""
|
||||||
|
return file_path in self.get_cyclic_files()
|
||||||
|
|
||||||
|
def get_cycle_chains(self) -> dict[Path, list[list[Path]]]:
|
||||||
|
"""Get all cycles grouped by their involved files."""
|
||||||
|
chains: dict[Path, list[list[Path]]] = {}
|
||||||
|
for cycle in self.detect_all_cycles():
|
||||||
|
for file in cycle:
|
||||||
|
if file not in chains:
|
||||||
|
chains[file] = []
|
||||||
|
chains[file].append(cycle)
|
||||||
|
return chains
|
||||||
|
|
||||||
|
def get_report(self) -> dict[str, Any]:
|
||||||
|
"""Generate a comprehensive cycle detection report."""
|
||||||
|
cycles = self.detect_all_cycles()
|
||||||
|
cyclic_files = self.get_cyclic_files()
|
||||||
|
|
||||||
|
report = {
|
||||||
|
"total_cycles": len(cycles),
|
||||||
|
"cyclic_files": len(cyclic_files),
|
||||||
|
"cyclic_file_names": [str(f) for f in cyclic_files],
|
||||||
|
"cycles": [
|
||||||
|
[str(f) for f in cycle] for cycle in cycles
|
||||||
|
],
|
||||||
|
"severity": self._calculate_severity(cycles),
|
||||||
|
}
|
||||||
|
|
||||||
|
return report
|
||||||
|
|
||||||
|
def _calculate_severity(
|
||||||
|
self, cycles: list[list[Path]]
|
||||||
|
) -> str:
|
||||||
|
"""Calculate the severity of the cyclic dependencies."""
|
||||||
|
if not cycles:
|
||||||
|
return "none"
|
||||||
|
|
||||||
|
max_cycle_length = max(len(c) for c in cycles) if cycles else 0
|
||||||
|
|
||||||
|
if max_cycle_length > 10:
|
||||||
|
return "critical"
|
||||||
|
elif max_cycle_length > 5:
|
||||||
|
return "high"
|
||||||
|
elif len(cycles) > 5:
|
||||||
|
return "medium"
|
||||||
|
return "low"
|
||||||
|
|
||||||
|
def export_cycle_report(self, path: Path) -> None:
|
||||||
|
"""Export the cycle report to a file."""
|
||||||
|
import json
|
||||||
|
|
||||||
|
report = self.get_report()
|
||||||
|
with open(path, "w") as f:
|
||||||
|
json.dump(report, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def find_cyclic_dependencies(
|
||||||
|
project_root: Path,
|
||||||
|
extensions: Optional[list[str]] = None,
|
||||||
|
) -> list[list[Path]]:
|
||||||
|
"""Convenience function to find cyclic dependencies."""
|
||||||
|
graph = DependencyGraph(project_root)
|
||||||
|
graph.build_from_directory(extensions=extensions)
|
||||||
|
|
||||||
|
detector = CycleDetector(graph)
|
||||||
|
return detector.detect_all_cycles()
|
||||||
|
|
||||||
|
|
||||||
|
def get_cyclic_file_report(
|
||||||
|
project_root: Path,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Generate a report on cyclic dependencies for a project."""
|
||||||
|
graph = DependencyGraph(project_root)
|
||||||
|
graph.build_from_directory()
|
||||||
|
|
||||||
|
detector = CycleDetector(graph)
|
||||||
|
return detector.get_report()
|
||||||
Reference in New Issue
Block a user