File size: 2,018 Bytes
3e802a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

import ast
from pathlib import Path
from typing import List, Set, Callable # Add Callable
import networkx as nx

def resolve_import_path(current_file: Path, module_name: str, level: int, project_root: Path) -> Path | None:
    # ... (function content remains unchanged) ...
    if level > 0:  # Relative import
        base_path = current_file.parent
        for _ in range(level - 1):
            base_path = base_path.parent
        
        module_path = base_path / Path(*module_name.split('.'))
    else:  # Absolute import
        module_path = project_root / Path(*module_name.split('.'))

    # Try to find the file (.py) or package (__init__.py)
    if module_path.with_suffix(".py").exists():
        return module_path.with_suffix(".py")
    if (module_path / "__init__.py").exists():
        return module_path / "__init__.py"
    
    return None

def build_dependency_graph(file_paths: List[Path], project_root: Path, log_callback: Callable[[str], None] = print) -> nx.DiGraph:
    """
    Builds a dependency graph from a list of Python files.
    Uses a callback for logging warnings.
    """
    graph = nx.DiGraph()
    path_map = {p.stem: p for p in file_paths} # Simplified mapping
    
    for file_path in file_paths:
        graph.add_node(file_path)
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
            tree = ast.parse(content)
            
            for node in ast.walk(tree):
                if isinstance(node, ast.ImportFrom):
                    if node.module:
                        dep_path = resolve_import_path(file_path, node.module, node.level, project_root)
                        if dep_path and dep_path in file_paths:
                            graph.add_edge(file_path, dep_path)

        except Exception as e:
            # Use the callback instead of print
            log_callback(f"Warning: Could not parse {file_path.name} for dependencies. Skipping. Error: {e}")
            
    return graph