Skip to main content
Glama
python.ts3.83 kB
/** * Python deep code analyzer using Python's native AST module */ import { spawn } from "child_process"; import * as path from "path"; import { CodeAnalyzer } from "../core/analyzer.js"; import { DeepAnalysis, FileAnalysis, AnalysisSummary } from "../core/types.js"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export class PythonAnalyzer extends CodeAnalyzer { private pythonHelperPath: string; constructor(projectPath: string, files: string[]) { super(projectPath, files); // Use path relative to source directory, not dist this.pythonHelperPath = path.resolve( __dirname, "..", "..", "src", "analyzers", "helpers", "python_analyzer.py" ); } canAnalyze(filePath: string): boolean { const ext = path.extname(filePath).toLowerCase(); return ext === ".py"; } getLanguage(): string { return "python"; } async analyze(): Promise<DeepAnalysis> { const fileAnalyses: FileAnalysis[] = []; for (const file of this.files) { if (this.canAnalyze(file)) { const analysis = await this.analyzeFile(file); if (analysis) { fileAnalyses.push(analysis); } } } return { language: "python", files: fileAnalyses, summary: this.generateSummary(fileAnalyses), }; } async analyzeFile(filePath: string): Promise<FileAnalysis | null> { try { const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(this.projectPath, filePath); const result = await this.runPythonAnalyzer(absolutePath); if (result.error) { console.error(`Error analyzing ${filePath}: ${result.error}`); return null; } return result as FileAnalysis; } catch (error) { console.error(`Error analyzing ${filePath}:`, error); return null; } } private runPythonAnalyzer(filePath: string): Promise<any> { return new Promise((resolve, reject) => { const python = spawn("python", [this.pythonHelperPath, filePath]); let stdout = ""; let stderr = ""; python.stdout.on("data", (data) => { stdout += data.toString(); }); python.stderr.on("data", (data) => { stderr += data.toString(); }); python.on("close", (code) => { if (code !== 0) { reject(new Error(`Python process exited with code ${code}: ${stderr}`)); return; } try { const result = JSON.parse(stdout); resolve(result); } catch (error) { reject(new Error(`Failed to parse Python analyzer output: ${error}`)); } }); python.on("error", (error) => { reject(new Error(`Failed to start Python process: ${error.message}`)); }); }); } private generateSummary(fileAnalyses: FileAnalysis[]): AnalysisSummary { const summary: AnalysisSummary = { totalFiles: fileAnalyses.length, totalClasses: 0, totalInterfaces: 0, totalFunctions: 0, totalEnums: 0, totalTypeAliases: 0, overallDocCoverage: 0, }; let totalSymbols = 0; let documentedSymbols = 0; fileAnalyses.forEach((file) => { summary.totalClasses += file.classes.length; summary.totalInterfaces += file.interfaces.length; summary.totalFunctions += file.functions.length; summary.totalEnums += file.enums.length; summary.totalTypeAliases += file.typeAliases.length; totalSymbols += file.documentation.totalSymbols; documentedSymbols += file.documentation.documentedSymbols; }); summary.overallDocCoverage = totalSymbols > 0 ? (documentedSymbols / totalSymbols) * 100 : 0; return summary; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ThaLoc0one/documentation-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server