skylos 1.2.1__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of skylos might be problematic. Click here for more details.
- skylos/__init__.py +1 -1
- skylos/analyzer.py +58 -92
- skylos/cli.py +46 -36
- skylos/constants.py +25 -10
- skylos/framework_aware.py +8 -2
- skylos/server.py +560 -0
- skylos/test_aware.py +0 -1
- skylos/visitor.py +70 -56
- {skylos-1.2.1.dist-info → skylos-2.0.0.dist-info}/METADATA +1 -1
- {skylos-1.2.1.dist-info → skylos-2.0.0.dist-info}/RECORD +13 -12
- {skylos-1.2.1.dist-info → skylos-2.0.0.dist-info}/WHEEL +0 -0
- {skylos-1.2.1.dist-info → skylos-2.0.0.dist-info}/entry_points.txt +0 -0
- {skylos-1.2.1.dist-info → skylos-2.0.0.dist-info}/top_level.txt +0 -0
skylos/framework_aware.py
CHANGED
|
@@ -2,7 +2,6 @@ import ast
|
|
|
2
2
|
import fnmatch
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
# decorator patterns for each of the more popular frameworks
|
|
6
5
|
FRAMEWORK_DECORATORS = [
|
|
7
6
|
# flask
|
|
8
7
|
"@app.route", "@app.get", "@app.post", "@app.put", "@app.delete",
|
|
@@ -83,14 +82,17 @@ class FrameworkAwareVisitor:
|
|
|
83
82
|
def visit_ImportFrom(self, node):
|
|
84
83
|
if node.module:
|
|
85
84
|
module_name = node.module.split('.')[0].lower()
|
|
85
|
+
|
|
86
86
|
if module_name in FRAMEWORK_IMPORTS:
|
|
87
87
|
self.is_framework_file = True
|
|
88
88
|
self.detected_frameworks.add(module_name)
|
|
89
|
+
|
|
89
90
|
self.generic_visit(node)
|
|
90
91
|
|
|
91
92
|
def visit_FunctionDef(self, node):
|
|
92
93
|
for deco in node.decorator_list:
|
|
93
94
|
decorator_str = self._normalize_decorator(deco)
|
|
95
|
+
|
|
94
96
|
if self._matches_framework_pattern(decorator_str, FRAMEWORK_DECORATORS):
|
|
95
97
|
self.framework_decorated_lines.add(node.lineno)
|
|
96
98
|
self.is_framework_file = True
|
|
@@ -116,13 +118,17 @@ class FrameworkAwareVisitor:
|
|
|
116
118
|
def _normalize_decorator(self, decorator_node):
|
|
117
119
|
if isinstance(decorator_node, ast.Name):
|
|
118
120
|
return f"@{decorator_node.id}"
|
|
121
|
+
|
|
119
122
|
elif isinstance(decorator_node, ast.Attribute):
|
|
120
123
|
if isinstance(decorator_node.value, ast.Name):
|
|
121
124
|
return f"@{decorator_node.value.id}.{decorator_node.attr}"
|
|
125
|
+
|
|
122
126
|
else:
|
|
123
127
|
return f"@{decorator_node.attr}"
|
|
128
|
+
|
|
124
129
|
elif isinstance(decorator_node, ast.Call):
|
|
125
130
|
return self._normalize_decorator(decorator_node.func)
|
|
131
|
+
|
|
126
132
|
return "@unknown"
|
|
127
133
|
|
|
128
134
|
def _matches_framework_pattern(self, text, patterns):
|
|
@@ -141,7 +147,7 @@ class FrameworkAwareVisitor:
|
|
|
141
147
|
except:
|
|
142
148
|
pass
|
|
143
149
|
|
|
144
|
-
def detect_framework_usage(definition,
|
|
150
|
+
def detect_framework_usage(definition, visitor=None):
|
|
145
151
|
if not visitor:
|
|
146
152
|
return None
|
|
147
153
|
|
skylos/server.py
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Skylos Web Server
|
|
4
|
+
Serves the frontend and provides API to analyze projects using skylos
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from flask import Flask, request, jsonify
|
|
8
|
+
from flask_cors import CORS
|
|
9
|
+
import skylos
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import webbrowser
|
|
13
|
+
from threading import Timer
|
|
14
|
+
|
|
15
|
+
app = Flask(__name__)
|
|
16
|
+
CORS(app)
|
|
17
|
+
|
|
18
|
+
@app.route('/')
|
|
19
|
+
def serve_frontend():
|
|
20
|
+
"""Serve the frontend HTML"""
|
|
21
|
+
return """<!DOCTYPE html>
|
|
22
|
+
<html lang="en">
|
|
23
|
+
<head>
|
|
24
|
+
<meta charset="UTF-8">
|
|
25
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
26
|
+
<title>Skylos Dead Code Analyzer</title>
|
|
27
|
+
<style>
|
|
28
|
+
* {
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 0;
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
body {
|
|
35
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', sans-serif;
|
|
36
|
+
background: #000000;
|
|
37
|
+
color: #ffffff;
|
|
38
|
+
min-height: 100vh;
|
|
39
|
+
padding: 20px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.container {
|
|
43
|
+
max-width: 1400px;
|
|
44
|
+
margin: 0 auto;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.header {
|
|
48
|
+
text-align: center;
|
|
49
|
+
margin-bottom: 40px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.header h1 {
|
|
53
|
+
font-size: 3rem;
|
|
54
|
+
margin-bottom: 10px;
|
|
55
|
+
color: #ffffff;
|
|
56
|
+
font-weight: 300;
|
|
57
|
+
letter-spacing: -1px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.header p {
|
|
61
|
+
color: #888888;
|
|
62
|
+
font-size: 1.1rem;
|
|
63
|
+
margin-top: 10px;
|
|
64
|
+
font-weight: 400;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.controls {
|
|
68
|
+
background: #111111;
|
|
69
|
+
border: 1px solid #333333;
|
|
70
|
+
border-radius: 12px;
|
|
71
|
+
padding: 32px;
|
|
72
|
+
margin-bottom: 32px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.folder-input {
|
|
76
|
+
margin-bottom: 25px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.folder-input label {
|
|
80
|
+
display: block;
|
|
81
|
+
font-weight: bold;
|
|
82
|
+
margin-bottom: 10px;
|
|
83
|
+
color: #ffffff;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.folder-input input {
|
|
87
|
+
width: 100%;
|
|
88
|
+
padding: 16px;
|
|
89
|
+
background: #222222;
|
|
90
|
+
color: #ffffff;
|
|
91
|
+
border: 1px solid #444444;
|
|
92
|
+
border-radius: 8px;
|
|
93
|
+
font-family: inherit;
|
|
94
|
+
font-size: 14px;
|
|
95
|
+
transition: border-color 0.2s ease;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.folder-input input:focus {
|
|
99
|
+
outline: none;
|
|
100
|
+
border-color: #ffffff;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.folder-input input::placeholder {
|
|
104
|
+
color: #888888;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.analyze-btn {
|
|
108
|
+
background: #ffffff;
|
|
109
|
+
color: #000000;
|
|
110
|
+
border: none;
|
|
111
|
+
padding: 16px 32px;
|
|
112
|
+
border-radius: 8px;
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
font-weight: 500;
|
|
115
|
+
font-family: inherit;
|
|
116
|
+
font-size: 14px;
|
|
117
|
+
margin-right: 15px;
|
|
118
|
+
transition: all 0.2s ease;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.analyze-btn:hover {
|
|
122
|
+
background: #f0f0f0;
|
|
123
|
+
transform: translateY(-1px);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.analyze-btn:disabled {
|
|
127
|
+
background: #666666;
|
|
128
|
+
color: #ffffff;
|
|
129
|
+
cursor: not-allowed;
|
|
130
|
+
transform: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.confidence-control {
|
|
134
|
+
margin-top: 25px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.confidence-control label {
|
|
138
|
+
display: block;
|
|
139
|
+
font-weight: bold;
|
|
140
|
+
margin-bottom: 10px;
|
|
141
|
+
color: #ffffff;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.confidence-slider {
|
|
145
|
+
width: 100%;
|
|
146
|
+
height: 6px;
|
|
147
|
+
background: #333333;
|
|
148
|
+
outline: none;
|
|
149
|
+
border-radius: 3px;
|
|
150
|
+
-webkit-appearance: none;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.confidence-slider::-webkit-slider-thumb {
|
|
154
|
+
-webkit-appearance: none;
|
|
155
|
+
appearance: none;
|
|
156
|
+
width: 20px;
|
|
157
|
+
height: 20px;
|
|
158
|
+
border-radius: 50%;
|
|
159
|
+
background: #ffffff;
|
|
160
|
+
cursor: pointer;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.confidence-slider::-moz-range-thumb {
|
|
164
|
+
width: 20px;
|
|
165
|
+
height: 20px;
|
|
166
|
+
border-radius: 50%;
|
|
167
|
+
background: #ffffff;
|
|
168
|
+
cursor: pointer;
|
|
169
|
+
border: none;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.summary {
|
|
173
|
+
background: #111111;
|
|
174
|
+
border: 1px solid #333333;
|
|
175
|
+
border-radius: 8px;
|
|
176
|
+
padding: 25px;
|
|
177
|
+
margin-bottom: 30px;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.summary h2 {
|
|
181
|
+
margin-bottom: 20px;
|
|
182
|
+
color: #ffffff;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.summary-grid {
|
|
186
|
+
display: grid;
|
|
187
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
188
|
+
gap: 15px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.summary-item {
|
|
192
|
+
background: #222222;
|
|
193
|
+
border: 1px solid #444444;
|
|
194
|
+
border-radius: 4px;
|
|
195
|
+
padding: 15px;
|
|
196
|
+
text-align: center;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.summary-item .count {
|
|
200
|
+
font-size: 2rem;
|
|
201
|
+
font-weight: bold;
|
|
202
|
+
color: #ffffff;
|
|
203
|
+
margin-bottom: 5px;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.summary-item .label {
|
|
207
|
+
color: #cccccc;
|
|
208
|
+
text-transform: uppercase;
|
|
209
|
+
font-size: 0.9rem;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.results {
|
|
213
|
+
background: #111111;
|
|
214
|
+
border: 1px solid #333333;
|
|
215
|
+
border-radius: 8px;
|
|
216
|
+
padding: 25px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.results h2 {
|
|
220
|
+
margin-bottom: 20px;
|
|
221
|
+
color: #ffffff;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.dead-code-list {
|
|
225
|
+
max-height: 600px;
|
|
226
|
+
overflow-y: auto;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.dead-code-item {
|
|
230
|
+
background: #222222;
|
|
231
|
+
border: 1px solid #444444;
|
|
232
|
+
border-radius: 4px;
|
|
233
|
+
padding: 15px;
|
|
234
|
+
margin-bottom: 10px;
|
|
235
|
+
display: flex;
|
|
236
|
+
justify-content: space-between;
|
|
237
|
+
align-items: center;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.item-details {
|
|
241
|
+
flex-grow: 1;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.item-name {
|
|
245
|
+
font-weight: bold;
|
|
246
|
+
color: #ffffff;
|
|
247
|
+
margin-bottom: 5px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.item-location {
|
|
251
|
+
color: #999999;
|
|
252
|
+
font-size: 0.9rem;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.item-meta {
|
|
256
|
+
display: flex;
|
|
257
|
+
align-items: center;
|
|
258
|
+
gap: 10px;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.item-type {
|
|
262
|
+
background: #ffffff;
|
|
263
|
+
color: #000000;
|
|
264
|
+
padding: 6px 12px;
|
|
265
|
+
border-radius: 6px;
|
|
266
|
+
font-size: 0.8rem;
|
|
267
|
+
font-weight: 500;
|
|
268
|
+
text-transform: uppercase;
|
|
269
|
+
letter-spacing: 0.5px;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.item-confidence {
|
|
273
|
+
color: #ffffff;
|
|
274
|
+
font-weight: bold;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.no-results {
|
|
278
|
+
text-align: center;
|
|
279
|
+
padding: 40px;
|
|
280
|
+
color: #666666;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.loading {
|
|
284
|
+
text-align: center;
|
|
285
|
+
padding: 40px;
|
|
286
|
+
color: #ffffff;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.error {
|
|
290
|
+
background: #330000;
|
|
291
|
+
border: 1px solid #660000;
|
|
292
|
+
color: #ff6666;
|
|
293
|
+
padding: 15px;
|
|
294
|
+
border-radius: 4px;
|
|
295
|
+
margin-bottom: 20px;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
::-webkit-scrollbar {
|
|
299
|
+
width: 8px;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
::-webkit-scrollbar-track {
|
|
303
|
+
background: #222222;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
::-webkit-scrollbar-thumb {
|
|
307
|
+
background: #444444;
|
|
308
|
+
border-radius: 4px;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
::-webkit-scrollbar-thumb:hover {
|
|
312
|
+
background: #666666;
|
|
313
|
+
}
|
|
314
|
+
</style>
|
|
315
|
+
</head>
|
|
316
|
+
<body>
|
|
317
|
+
<div class="container">
|
|
318
|
+
<div class="header">
|
|
319
|
+
<h1>Skylos Dead Code Analyzer</h1>
|
|
320
|
+
<p>Find and eliminate unused code in your Python projects</p>
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<div class="controls">
|
|
324
|
+
<div class="folder-input">
|
|
325
|
+
<label for="folderPath">Project Path:</label>
|
|
326
|
+
<input type="text" id="folderPath" placeholder="/path/to/your/python/project" value="./">
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<button class="analyze-btn" id="analyzeBtn">Analyze Project</button>
|
|
330
|
+
|
|
331
|
+
<div class="confidence-control">
|
|
332
|
+
<label for="confidenceSlider">Confidence Threshold: <span id="confidenceValue">60</span>%</label>
|
|
333
|
+
<input type="range" id="confidenceSlider" class="confidence-slider"
|
|
334
|
+
min="0" max="100" value="60" step="1">
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
<div id="errorMessage"></div>
|
|
339
|
+
|
|
340
|
+
<div class="summary">
|
|
341
|
+
<h2>Summary</h2>
|
|
342
|
+
<div class="summary-grid">
|
|
343
|
+
<div class="summary-item">
|
|
344
|
+
<div class="count" id="functionsCount">0</div>
|
|
345
|
+
<div class="label">Unreachable Functions</div>
|
|
346
|
+
</div>
|
|
347
|
+
<div class="summary-item">
|
|
348
|
+
<div class="count" id="importsCount">0</div>
|
|
349
|
+
<div class="label">Unused Imports</div>
|
|
350
|
+
</div>
|
|
351
|
+
<div class="summary-item">
|
|
352
|
+
<div class="count" id="parametersCount">0</div>
|
|
353
|
+
<div class="label">Unused Parameters</div>
|
|
354
|
+
</div>
|
|
355
|
+
<div class="summary-item">
|
|
356
|
+
<div class="count" id="variablesCount">0</div>
|
|
357
|
+
<div class="label">Unused Variables</div>
|
|
358
|
+
</div>
|
|
359
|
+
<div class="summary-item">
|
|
360
|
+
<div class="count" id="classesCount">0</div>
|
|
361
|
+
<div class="label">Unused Classes</div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<div class="results">
|
|
367
|
+
<h2>Dead Code Items</h2>
|
|
368
|
+
<div class="dead-code-list" id="deadCodeList">
|
|
369
|
+
<div class="no-results">
|
|
370
|
+
<p>Enter a project path and click "Analyze Project" to scan for dead code.</p>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<script>
|
|
377
|
+
let analysisData = null;
|
|
378
|
+
let confidenceThreshold = 60;
|
|
379
|
+
|
|
380
|
+
const slider = document.getElementById('confidenceSlider');
|
|
381
|
+
const confidenceValue = document.getElementById('confidenceValue');
|
|
382
|
+
const analyzeBtn = document.getElementById('analyzeBtn');
|
|
383
|
+
const folderPath = document.getElementById('folderPath');
|
|
384
|
+
const errorMessage = document.getElementById('errorMessage');
|
|
385
|
+
|
|
386
|
+
slider.addEventListener('input', (e) => {
|
|
387
|
+
confidenceThreshold = parseInt(e.target.value);
|
|
388
|
+
confidenceValue.textContent = confidenceThreshold;
|
|
389
|
+
if (analysisData) {
|
|
390
|
+
updateDisplay();
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
analyzeBtn.addEventListener('click', analyzeProject);
|
|
395
|
+
|
|
396
|
+
function showError(message) {
|
|
397
|
+
errorMessage.innerHTML = `<div class="error">${message}</div>`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function clearError() {
|
|
401
|
+
errorMessage.innerHTML = '';
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
async function analyzeProject() {
|
|
405
|
+
const path = folderPath.value.trim();
|
|
406
|
+
if (!path) {
|
|
407
|
+
showError('Please enter a project path');
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
clearError();
|
|
412
|
+
analyzeBtn.textContent = 'Analyzing...';
|
|
413
|
+
analyzeBtn.disabled = true;
|
|
414
|
+
|
|
415
|
+
document.getElementById('deadCodeList').innerHTML = '<div class="loading">Analyzing project...</div>';
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
const response = await fetch('/api/analyze', {
|
|
419
|
+
method: 'POST',
|
|
420
|
+
headers: {
|
|
421
|
+
'Content-Type': 'application/json',
|
|
422
|
+
},
|
|
423
|
+
body: JSON.stringify({
|
|
424
|
+
path: path,
|
|
425
|
+
confidence: confidenceThreshold
|
|
426
|
+
})
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
if (!response.ok) {
|
|
430
|
+
const errorData = await response.json();
|
|
431
|
+
throw new Error(errorData.error || `Analysis failed: ${response.statusText}`);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const result = await response.json();
|
|
435
|
+
analysisData = result;
|
|
436
|
+
updateDisplay();
|
|
437
|
+
|
|
438
|
+
} catch (error) {
|
|
439
|
+
showError(`Error: ${error.message}`);
|
|
440
|
+
document.getElementById('deadCodeList').innerHTML = '<div class="no-results"><p>Analysis failed. Check the error message above.</p></div>';
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
analyzeBtn.textContent = 'Analyze Project';
|
|
444
|
+
analyzeBtn.disabled = false;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function updateDisplay() {
|
|
448
|
+
if (!analysisData) return;
|
|
449
|
+
|
|
450
|
+
const filteredData = getFilteredData();
|
|
451
|
+
updateSummary(filteredData);
|
|
452
|
+
updateDeadCodeList(filteredData);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function getFilteredData() {
|
|
456
|
+
const data = {
|
|
457
|
+
functions: analysisData.unused_functions || [],
|
|
458
|
+
imports: analysisData.unused_imports || [],
|
|
459
|
+
parameters: analysisData.unused_parameters || [],
|
|
460
|
+
variables: analysisData.unused_variables || [],
|
|
461
|
+
classes: analysisData.unused_classes || []
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// Filter by confidence threshold
|
|
465
|
+
Object.keys(data).forEach(key => {
|
|
466
|
+
data[key] = data[key].filter(item => item.confidence >= confidenceThreshold);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
return data;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function updateSummary(data) {
|
|
473
|
+
document.getElementById('functionsCount').textContent = data.functions.length;
|
|
474
|
+
document.getElementById('importsCount').textContent = data.imports.length;
|
|
475
|
+
document.getElementById('parametersCount').textContent = data.parameters.length;
|
|
476
|
+
document.getElementById('variablesCount').textContent = data.variables.length;
|
|
477
|
+
document.getElementById('classesCount').textContent = data.classes.length;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function updateDeadCodeList(data) {
|
|
481
|
+
const listElement = document.getElementById('deadCodeList');
|
|
482
|
+
const allItems = [];
|
|
483
|
+
|
|
484
|
+
// Combine all items with their categories
|
|
485
|
+
Object.keys(data).forEach(category => {
|
|
486
|
+
data[category].forEach(item => {
|
|
487
|
+
allItems.push({
|
|
488
|
+
...item,
|
|
489
|
+
category: category.slice(0, -1) // Remove 's' from end
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
if (allItems.length === 0) {
|
|
495
|
+
listElement.innerHTML = `
|
|
496
|
+
<div class="no-results">
|
|
497
|
+
<p>No dead code found at confidence level ${confidenceThreshold}%</p>
|
|
498
|
+
</div>
|
|
499
|
+
`;
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Sort by confidence (highest first)
|
|
504
|
+
allItems.sort((a, b) => b.confidence - a.confidence);
|
|
505
|
+
|
|
506
|
+
listElement.innerHTML = allItems.map(item => `
|
|
507
|
+
<div class="dead-code-item">
|
|
508
|
+
<div class="item-details">
|
|
509
|
+
<div class="item-name">${item.name}</div>
|
|
510
|
+
<div class="item-location">${item.file}:${item.line}</div>
|
|
511
|
+
</div>
|
|
512
|
+
<div class="item-meta">
|
|
513
|
+
<span class="item-type">${item.category}</span>
|
|
514
|
+
<span class="item-confidence">${item.confidence}%</span>
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
`).join('');
|
|
518
|
+
}
|
|
519
|
+
</script>
|
|
520
|
+
</body>
|
|
521
|
+
</html>"""
|
|
522
|
+
|
|
523
|
+
@app.route('/api/analyze', methods=['POST'])
|
|
524
|
+
def analyze_project():
|
|
525
|
+
"""Analyze a project using skylos"""
|
|
526
|
+
try:
|
|
527
|
+
data = request.json
|
|
528
|
+
path = data.get('path')
|
|
529
|
+
confidence = data.get('confidence', 60)
|
|
530
|
+
|
|
531
|
+
if not path:
|
|
532
|
+
return jsonify({'error': 'Path is required'}), 400
|
|
533
|
+
|
|
534
|
+
if not os.path.exists(path):
|
|
535
|
+
return jsonify({'error': f'Path does not exist: {path}'}), 400
|
|
536
|
+
|
|
537
|
+
# Call your actual skylos analyzer
|
|
538
|
+
result_json = skylos.analyze(path, conf=confidence)
|
|
539
|
+
result = json.loads(result_json)
|
|
540
|
+
|
|
541
|
+
return jsonify(result)
|
|
542
|
+
|
|
543
|
+
except Exception as e:
|
|
544
|
+
return jsonify({'error': str(e)}), 500
|
|
545
|
+
|
|
546
|
+
def start_server():
|
|
547
|
+
"""Start the web server"""
|
|
548
|
+
def open_browser():
|
|
549
|
+
webbrowser.open('http://localhost:5090')
|
|
550
|
+
|
|
551
|
+
print(" Starting Skylos Web Interface...")
|
|
552
|
+
print("Opening browser at: http://localhost:5090")
|
|
553
|
+
|
|
554
|
+
# Open browser after a short delay
|
|
555
|
+
Timer(1.5, open_browser).start()
|
|
556
|
+
|
|
557
|
+
app.run(debug=False, host='0.0.0.0', port=5090, use_reloader=False)
|
|
558
|
+
|
|
559
|
+
if __name__ == '__main__':
|
|
560
|
+
start_server()
|
skylos/test_aware.py
CHANGED