怎样实现解析模块依赖关系脚本

wen 实用脚本 25

本文目录导读:

怎样实现解析模块依赖关系脚本

  1. Python 实现(推荐)
  2. JavaScript/Node.js 实现
  3. 增强版 Python 脚本(支持多种格式)
  4. 使用示例

我来帮你实现一个解析模块依赖关系的脚本,这里提供几种常见编程语言的实现方案:

Python 实现(推荐)

import json
import re
from collections import defaultdict, deque
class ModuleDependencyParser:
    def __init__(self):
        self.modules = {}  # 模块及其依赖
        self.dependencies = defaultdict(set)  # 依赖关系图
    def parse_python_imports(self, file_path):
        """解析Python文件的导入语句"""
        imports = []
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        # 匹配 import 语句
        import_patterns = [
            r'^import\s+(\S+)',  # import module
            r'^from\s+(\S+)\s+import',  # from module import
            r'^from\s+(\S+)\s+import\s+\('  # from module import (
        ]
        for line in content.split('\n'):
            for pattern in import_patterns:
                match = re.match(pattern, line.strip())
                if match:
                    module_name = match.group(1).split('.')[0]
                    if module_name != file_path.split('/')[-1].replace('.py', ''):
                        imports.append(module_name)
        return imports
    def parse_json_dependencies(self, json_file):
        """解析 package.json 等依赖文件"""
        with open(json_file, 'r', encoding='utf-8') as f:
            data = json.load(f)
        if 'dependencies' in data:
            return list(data['dependencies'].keys())
        return []
    def analyze_dependencies(self, files_list):
        """分析一系列文件的依赖关系"""
        for file_path in files_list:
            if file_path.endswith('.py'):
                deps = self.parse_python_imports(file_path)
            elif file_path.endswith('package.json'):
                deps = self.parse_json_dependencies(file_path)
            else:
                continue
            module_name = file_path.split('/')[-1]
            self.modules[module_name] = file_path
            self.dependencies[module_name].update(deps)
        return self.dependencies
    def topological_sort(self):
        """拓扑排序,找出依赖顺序"""
        # 计算入度
        in_degree = defaultdict(int)
        for module, deps in self.dependencies.items():
            for dep in deps:
                in_degree[dep] += 1
        # 初始入度为0的节点
        queue = deque()
        for module in self.modules:
            if in_degree[module] == 0:
                queue.append(module)
        # 拓扑排序
        result = []
        while queue:
            module = queue.popleft()
            result.append(module)
            for dep in self.dependencies[module]:
                in_degree[dep] -= 1
                if in_degree[dep] == 0:
                    queue.append(dep)
        return result
    def find_circular_dependencies(self):
        """检测循环依赖"""
        visited = set()
        rec_stack = set()
        circular_deps = []
        def dfs(module):
            visited.add(module)
            rec_stack.add(module)
            for dep in self.dependencies.get(module, []):
                if dep in self.modules:  # 只检查项目内部模块
                    if dep not in visited:
                        if dfs(dep):
                            return True
                    elif dep in rec_stack:
                        circular_deps.append((module, dep))
                        return True
            rec_stack.remove(module)
            return False
        for module in self.modules:
            if module not in visited:
                dfs(module)
        return circular_deps
    def generate_dependency_tree(self, root_module):
        """生成依赖树"""
        tree = {root_module: []}
        visited = set()
        def build_tree(module, depth=0):
            if module in visited:
                return
            visited.add(module)
            for dep in self.dependencies.get(module, []):
                if dep in self.modules:
                    tree[module].append(dep)
                    tree[dep] = []
                    build_tree(dep, depth + 1)
        build_tree(root_module)
        return tree
    def visualize(self, format='text'):
        """可视化依赖关系"""
        if format == 'text':
            for module, deps in self.dependencies.items():
                print(f"{'='*40}")
                print(f"模块: {module}")
                print(f"文件: {self.modules[module]}")
                if deps:
                    print(f"依赖: {', '.join(deps)}")
                else:
                    print("依赖: 无")
        elif format == 'mermaid':
            print("```mermaid")
            print("graph TD")
            for module, deps in self.dependencies.items():
                for dep in deps:
                    if dep in self.modules:
                        print(f"    {module}[{module}] --> {dep}[{dep}]")
            print("```")
# 使用示例
if __name__ == "__main__":
    parser = ModuleDependencyParser()
    # 示例文件列表
    files = [
        'main.py',
        'utils.py',
        'database.py',
        'config.py',
        'package.json'
    ]
    # 分析依赖
    parser.analyze_dependencies(files)
    # 可视化
    parser.visualize('text')
    # 检查循环依赖
    circular = parser.find_circular_dependencies()
    if circular:
        print(f"\n发现循环依赖: {circular}")
    else:
        print("\n无循环依赖")
    # 获取构建顺序
    build_order = parser.topological_sort()
    print(f"\n构建顺序: {build_order}")

JavaScript/Node.js 实现

const fs = require('fs');
const path = require('path');
class ModuleDependencyParser {
    constructor() {
        this.modules = new Map();
        this.dependencies = new Map();
    }
    parseJSImports(filePath) {
        const content = fs.readFileSync(filePath, 'utf-8');
        const imports = [];
        // 匹配 CommonJS require
        const requireRegex = /require\(['"]([^'"]+)['"]\)/g;
        let match;
        while ((match = requireRegex.exec(content)) !== null) {
            imports.push(match[1]);
        }
        // 匹配 ES6 import
        const importRegex = /import\s+(?:[\w*\s{},]*\s+from\s+)?['"]([^'"]+)['"]/g;
        while ((match = importRegex.exec(content)) !== null) {
            imports.push(match[1]);
        }
        return imports;
    }
    parsePackageJson(filePath) {
        const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
        return Object.keys(data.dependencies || {});
    }
    analyzeDirectory(dirPath) {
        const files = fs.readdirSync(dirPath);
        files.forEach(file => {
            const fullPath = path.join(dirPath, file);
            const stat = fs.statSync(fullPath);
            if (stat.isDirectory() && file !== 'node_modules') {
                this.analyzeDirectory(fullPath);
            } else if (file.endsWith('.js') || file.endsWith('.jsx') || file.endsWith('.ts')) {
                const moduleName = file;
                this.modules.set(moduleName, fullPath);
                const deps = this.parseJSImports(fullPath);
                this.dependencies.set(moduleName, new Set(deps.filter(dep => 
                    dep.startsWith('.') || dep.startsWith('/')
                )));
            }
        });
    }
    topologicalSort() {
        const inDegree = new Map();
        const queue = [];
        const result = [];
        // 计算入度
        for (const [module, deps] of this.dependencies) {
            if (!inDegree.has(module)) {
                inDegree.set(module, 0);
            }
            for (const dep of deps) {
                inDegree.set(dep, (inDegree.get(dep) || 0) + 1);
            }
        }
        // 找入度为0的节点
        for (const [module, degree] of inDegree) {
            if (degree === 0) {
                queue.push(module);
            }
        }
        // BFS
        while (queue.length > 0) {
            const module = queue.shift();
            result.push(module);
            const deps = this.dependencies.get(module) || new Set();
            for (const dep of deps) {
                const newDegree = (inDegree.get(dep) || 1) - 1;
                inDegree.set(dep, newDegree);
                if (newDegree === 0) {
                    queue.push(dep);
                }
            }
        }
        return result;
    }
    visualize() {
        console.log('=== 模块依赖关系 ===');
        for (const [module, deps] of this.dependencies) {
            console.log(`\n模块: ${module}`);
            console.log(`文件: ${this.modules.get(module)}`);
            console.log(`依赖: ${deps.size > 0 ? [...deps].join(', ') : '无'}`);
        }
    }
}
// 使用示例
const parser = new ModuleDependencyParser();
parser.analyzeDirectory('./src');
parser.visualize();
console.log('\n构建顺序:', parser.topologicalSort());

增强版 Python 脚本(支持多种格式)

#!/usr/bin/env python3
"""
模块依赖关系解析器 - 增强版
支持多种文件格式和输出方式
"""
import os
import sys
import json
import yaml
import re
from pathlib import Path
from typing import Dict, List, Set, Optional
from dataclasses import dataclass
from collections import defaultdict
@dataclass
class DependencyInfo:
    """依赖信息"""
    file_path: str
    dependencies: Set[str]
    module_type: str
class EnhancedDependencyParser:
    def __init__(self, project_root: str):
        self.project_root = Path(project_root)
        self.deps_info: Dict[str, DependencyInfo] = {}
        self.supported_extensions = {
            '.py': self._parse_python,
            '.js': self._parse_javascript,
            '.ts': self._parse_typescript,
            '.json': self._parse_json_deps,
            '.yaml': self._parse_yaml_deps,
            '.yml': self._parse_yaml_deps,
        }
    def scan_project(self, exclude_dirs: Optional[List[str]] = None):
        """扫描整个项目"""
        exclude_dirs = exclude_dirs or ['node_modules', '__pycache__', '.git', 'venv']
        for file_path in self.project_root.rglob('*'):
            # 跳过排除目录
            if any(exclude in str(file_path) for exclude in exclude_dirs):
                continue
            if file_path.suffix in self.supported_extensions:
                self._parse_file(file_path)
    def _parse_file(self, file_path: Path):
        """解析单个文件"""
        parser = self.supported_extensions.get(file_path.suffix)
        if parser:
            deps = parser(file_path)
            if deps:
                module_name = str(file_path.relative_to(self.project_root))
                self.deps_info[module_name] = DependencyInfo(
                    file_path=str(file_path),
                    dependencies=deps,
                    module_type=file_path.suffix
                )
    def _parse_python(self, file_path: Path) -> Set[str]:
        """解析Python文件"""
        deps = set()
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            # 匹配各种导入模式
            patterns = [
                r'^import\s+(\S+)',
                r'^from\s+(\S+)\s+import',
                r'^from\s+(\S+)\s+import\s+\(',
            ]
            for line in content.split('\n'):
                for pattern in patterns:
                    match = re.match(pattern, line.strip())
                    if match:
                        module = match.group(1).split('.')[0]
                        if not module.startswith('_'):
                            deps.add(module)
        except Exception as e:
            print(f"解析错误 {file_path}: {e}")
        return deps
    def _parse_javascript(self, file_path: Path) -> Set[str]:
        """解析JavaScript文件"""
        deps = set()
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            # 匹配 require
            for match in re.finditer(r"require\(['\"]([^'\"]+)['\"]\)", content):
                dep = match.group(1)
                if dep.startswith('.'):
                    deps.add(dep)
            # 匹配 import
            for match in re.finditer(
                r"import\s+(?:[\w*\s{},]*\s+from\s+)?['\"]([^'\"]+)['\"]",
                content
            ):
                dep = match.group(1)
                if dep.startswith('.'):
                    deps.add(dep)
        except Exception as e:
            print(f"解析错误 {file_path}: {e}")
        return deps
    def _parse_typescript(self, file_path: Path) -> Set[str]:
        """解析TypeScript文件(与JavaScript类似)"""
        return self._parse_javascript(file_path)
    def _parse_json_deps(self, file_path: Path) -> Set[str]:
        """解析JSON依赖文件"""
        deps = set()
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
            # 支持多种依赖类型
            for key in ['dependencies', 'devDependencies', 'peerDependencies']:
                if key in data:
                    deps.update(data[key].keys())
        except Exception as e:
            print(f"解析错误 {file_path}: {e}")
        return deps
    def _parse_yaml_deps(self, file_path: Path) -> Set[str]:
        """解析YAML依赖文件"""
        deps = set()
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                data = yaml.safe_load(f)
            if data:
                # 支持 pip 格式
                if isinstance(data, dict):
                    for key in ['dependencies', 'dev_dependencies']:
                        if key in data:
                            deps.update(data[key])
                elif isinstance(data, list):
                    # requirements.txt 格式转换
                    for item in data:
                        if isinstance(item, str) and '==' in item:
                            deps.add(item.split('==')[0])
        except Exception as e:
            print(f"解析错误 {file_path}: {e}")
        return deps
    def build_dependency_graph(self) -> Dict[str, List[str]]:
        """构建依赖图"""
        graph = defaultdict(list)
        for module, info in self.deps_info.items():
            graph[module] = list(info.dependencies)
        return dict(graph)
    def find_hot_modules(self, top_n: int = 10) -> List[tuple]:
        """找出被最多模块依赖的热门模块"""
        dep_count = defaultdict(int)
        for info in self.deps_info.values():
            for dep in info.dependencies:
                dep_count[dep] += 1
        return sorted(dep_count.items(), key=lambda x: x[1], reverse=True)[:top_n]
    def export_to_json(self, output_file: str):
        """导出为JSON格式"""
        export_data = {
            'project': str(self.project_root),
            'total_files': len(self.deps_info),
            'modules': {}
        }
        for module, info in self.deps_info.items():
            export_data['modules'][module] = {
                'file': info.file_path,
                'type': info.module_type,
                'dependencies': list(info.dependencies)
            }
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(export_data, f, indent=2)
    def generate_report(self):
        """生成分析报告"""
        print(f"\n{'='*60}")
        print(f"项目依赖分析报告")
        print(f"项目路径: {self.project_root}")
        print(f"找到 {len(self.deps_info)} 个模块")
        print(f"{'='*60}")
        # 按类型统计
        type_counts = defaultdict(int)
        for info in self.deps_info.values():
            type_counts[info.module_type] += 1
        print("\n文件类型分布:")
        for ext, count in sorted(type_counts.items()):
            print(f"  {ext}: {count} 个文件")
        # 热门模块
        print("\n热门模块(被依赖最多的):")
        hot_modules = self.find_hot_modules(5)
        for module, count in hot_modules:
            print(f"  {module}: 被 {count} 个模块依赖")
        # 孤立模块
        isolated = [m for m, info in self.deps_info.items() 
                   if not info.dependencies]
        if isolated:
            print(f"\n孤立模块(无依赖): {len(isolated)} 个")
    def visualize_mermaid(self):
        """生成Mermaid图表"""
        print("\n```mermaid")
        print("graph TD")
        for module, info in self.deps_info.items():
            for dep in info.dependencies:
                # 检查依赖是否存在
                if any(dep in m for m in self.deps_info.keys()):
                    print(f"    {module.replace('/', '_')}[{module}] --> {dep.replace('/', '_')}[{dep}]")
        print("```")
# 主函数
def main():
    if len(sys.argv) < 2:
        print("用法: python dependency_parser.py <项目路径> [选项]")
        print("选项:")
        print("  --json      导出JSON格式")
        print("  --mermaid   显示Mermaid图表")
        print("  --report    显示分析报告")
        sys.exit(1)
    project_path = sys.argv[1]
    parser = EnhancedDependencyParser(project_path)
    print(f"正在扫描项目: {project_path}")
    parser.scan_project()
    options = set(sys.argv[2:])
    if '--json' in options:
        output_file = 'dependencies.json'
        parser.export_to_json(output_file)
        print(f"已导出到 {output_file}")
    if '--mermaid' in options:
        parser.visualize_mermaid()
    if '--report' in options or not options:
        parser.generate_report()
if __name__ == "__main__":
    main()

使用示例

基础使用

# Python版本
python dependency_parser.py /path/to/project --report
# 导出JSON
python dependency_parser.py /path/to/project --json
# 生成图表
python dependency_parser.py /path/to/project --mermaid

Node.js版本

node dependency_parser.js

代码中调用

# Python 版本
parser = ModuleDependencyParser()
files = ['main.py', 'utils.py', 'database.py']
parser.analyze_dependencies(files)
# 获取依赖关系
dependencies = parser.dependencies
build_order = parser.topological_sort()
# 检查循环依赖
circular_deps = parser.find_circular_dependencies()

这个脚本支持:

  1. 多种文件格式(Python, JavaScript, TypeScript, JSON, YAML)
  2. 拓扑排序(构建顺序)
  3. 循环依赖检测
  4. 依赖树生成
  5. 多种输出格式(文本、JSON、Mermaid图表)
  6. 报告生成

你可以根据具体需求选择适合的实现版本。

抱歉,评论功能暂时关闭!