(filePaths, rootPath)
| 69 | } |
| 70 | |
| 71 | export async function analyzePythonFiles(filePaths, rootPath) { |
| 72 | const checks = []; |
| 73 | const metrics = []; |
| 74 | const artifacts = []; |
| 75 | |
| 76 | if (filePaths.length === 0) { |
| 77 | return { checks, metrics, artifacts }; |
| 78 | } |
| 79 | |
| 80 | const perFile = []; |
| 81 | let totalCommentLines = 0; |
| 82 | let totalCodeLines = 0; |
| 83 | let totalLongLines = 0; |
| 84 | let totalImports = 0; |
| 85 | let totalClasses = 0; |
| 86 | let maxComplexity = 0; |
| 87 | let maxFunctionLength = 0; |
| 88 | let maxNesting = 0; |
| 89 | let functionCount = 0; |
| 90 | let totalFunctionLength = 0; |
| 91 | let testFileCount = 0; |
| 92 | |
| 93 | for (const filePath of filePaths) { |
| 94 | const source = await readText(filePath); |
| 95 | const lines = source.replace(/\r\n/g, "\n").split("\n"); |
| 96 | const functions = detectFunctions(lines); |
| 97 | const commentLines = lines.filter((line) => line.trim().startsWith("#")).length; |
| 98 | const longLines = lines.filter((line) => line.length > 120).length; |
| 99 | const imports = lines.filter((line) => /^(\s*)(import|from)\b/.test(line)).length; |
| 100 | const classes = countMatches(source, /^\s*class\s+[A-Za-z0-9_]+/gm); |
| 101 | const complexity = 1 + countDecisionPoints(source); |
| 102 | const fileMaxComplexity = functions.reduce((max, fn) => Math.max(max, fn.complexity), complexity); |
| 103 | const fileMaxFunctionLength = functions.reduce((max, fn) => Math.max(max, fn.length), 0); |
| 104 | const fileMaxNesting = functions.reduce((max, fn) => Math.max(max, fn.nesting), 0); |
| 105 | |
| 106 | totalCommentLines += commentLines; |
| 107 | totalCodeLines += lines.filter((line) => line.trim()).length; |
| 108 | totalLongLines += longLines; |
| 109 | totalImports += imports; |
| 110 | totalClasses += classes; |
| 111 | maxComplexity = Math.max(maxComplexity, fileMaxComplexity); |
| 112 | maxFunctionLength = Math.max(maxFunctionLength, fileMaxFunctionLength); |
| 113 | maxNesting = Math.max(maxNesting, fileMaxNesting); |
| 114 | functionCount += functions.length; |
| 115 | totalFunctionLength += functions.reduce((sum, fn) => sum + fn.length, 0); |
| 116 | if (/(^|\/)(tests?|__tests__)\/|(^|\/)test_[^/]+\.py$/.test(filePath)) { |
| 117 | testFileCount += 1; |
| 118 | } |
| 119 | |
| 120 | perFile.push({ |
| 121 | path: relativePath(rootPath, filePath), |
| 122 | functionCount: functions.length, |
| 123 | complexity: fileMaxComplexity, |
| 124 | maxFunctionLength: fileMaxFunctionLength, |
| 125 | maxNesting: fileMaxNesting, |
| 126 | longLines, |
| 127 | }); |
| 128 | } |
no test coverage detected