skylos 1.2.2__py3-none-any.whl → 2.1.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/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
@@ -7,7 +7,6 @@ class TestAwareVisitor:
7
7
  self.is_test_file = False
8
8
  self.test_decorated_lines = set()
9
9
 
10
- # mark as test file based on the file path/name NOT imports
11
10
  if filename and TEST_FILE_RE.search(str(filename)):
12
11
  self.is_test_file = True
13
12