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