skylos 1.0.10__py3-none-any.whl → 2.5.2__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.
- skylos/__init__.py +9 -3
- skylos/analyzer.py +674 -168
- skylos/cfg_visitor.py +60 -0
- skylos/cli.py +719 -235
- skylos/codemods.py +277 -0
- skylos/config.py +50 -0
- skylos/constants.py +78 -0
- skylos/gatekeeper.py +147 -0
- skylos/linter.py +18 -0
- skylos/rules/base.py +20 -0
- skylos/rules/danger/calls.py +119 -0
- skylos/rules/danger/danger.py +157 -0
- skylos/rules/danger/danger_cmd/cmd_flow.py +75 -0
- skylos/rules/danger/danger_fs/__init__.py +0 -0
- skylos/rules/danger/danger_fs/path_flow.py +79 -0
- skylos/rules/danger/danger_net/__init__.py +0 -0
- skylos/rules/danger/danger_net/ssrf_flow.py +80 -0
- skylos/rules/danger/danger_sql/__init__.py +0 -0
- skylos/rules/danger/danger_sql/sql_flow.py +245 -0
- skylos/rules/danger/danger_sql/sql_raw_flow.py +96 -0
- skylos/rules/danger/danger_web/__init__.py +0 -0
- skylos/rules/danger/danger_web/xss_flow.py +170 -0
- skylos/rules/danger/taint.py +110 -0
- skylos/rules/quality/__init__.py +0 -0
- skylos/rules/quality/complexity.py +95 -0
- skylos/rules/quality/logic.py +96 -0
- skylos/rules/quality/nesting.py +101 -0
- skylos/rules/quality/structure.py +99 -0
- skylos/rules/secrets.py +325 -0
- skylos/server.py +554 -0
- skylos/visitor.py +502 -90
- skylos/visitors/__init__.py +0 -0
- skylos/visitors/framework_aware.py +437 -0
- skylos/visitors/test_aware.py +74 -0
- skylos-2.5.2.dist-info/METADATA +21 -0
- skylos-2.5.2.dist-info/RECORD +42 -0
- {skylos-1.0.10.dist-info → skylos-2.5.2.dist-info}/WHEEL +1 -1
- {skylos-1.0.10.dist-info → skylos-2.5.2.dist-info}/top_level.txt +0 -1
- skylos-1.0.10.dist-info/METADATA +0 -8
- skylos-1.0.10.dist-info/RECORD +0 -21
- test/compare_tools.py +0 -604
- test/diagnostics.py +0 -364
- test/sample_repo/app.py +0 -13
- test/sample_repo/sample_repo/commands.py +0 -81
- test/sample_repo/sample_repo/models.py +0 -122
- test/sample_repo/sample_repo/routes.py +0 -89
- test/sample_repo/sample_repo/utils.py +0 -36
- test/test_skylos.py +0 -456
- test/test_visitor.py +0 -220
- {test → skylos/rules}/__init__.py +0 -0
- {test/sample_repo → skylos/rules/danger}/__init__.py +0 -0
- {test/sample_repo/sample_repo → skylos/rules/danger/danger_cmd}/__init__.py +0 -0
- {skylos-1.0.10.dist-info → skylos-2.5.2.dist-info}/entry_points.txt +0 -0
test/diagnostics.py
DELETED
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
import sys
|
|
3
|
-
import json
|
|
4
|
-
import subprocess
|
|
5
|
-
import re
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
def load_ground_truth(path):
|
|
9
|
-
"""Load the ground truth data from a ground_truth.json file with the Skylos test structure"""
|
|
10
|
-
try:
|
|
11
|
-
with open(path, 'r') as f:
|
|
12
|
-
data = json.load(f)
|
|
13
|
-
|
|
14
|
-
dead_items = []
|
|
15
|
-
if "files" in data and "code.py" in data["files"]:
|
|
16
|
-
if "dead_items" in data["files"]["code.py"]:
|
|
17
|
-
return data["files"]["code.py"]["dead_items"]
|
|
18
|
-
|
|
19
|
-
return []
|
|
20
|
-
except Exception as e:
|
|
21
|
-
print(f"Error loading ground truth from {path}: {e}")
|
|
22
|
-
return []
|
|
23
|
-
|
|
24
|
-
def run_skylos_on_file(code_file, skylos_path):
|
|
25
|
-
try:
|
|
26
|
-
cmd = [sys.executable, skylos_path, str(code_file)]
|
|
27
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
28
|
-
|
|
29
|
-
if result.returncode != 0:
|
|
30
|
-
print(f"Error running Skylos subprocess: {result.stderr}")
|
|
31
|
-
return {}
|
|
32
|
-
|
|
33
|
-
json_start = result.stdout.find('{')
|
|
34
|
-
if json_start > -1:
|
|
35
|
-
json_str = result.stdout[json_start:]
|
|
36
|
-
try:
|
|
37
|
-
return json.loads(json_str)
|
|
38
|
-
except json.JSONDecodeError:
|
|
39
|
-
print(f"Error parsing Skylos JSON output")
|
|
40
|
-
return {}
|
|
41
|
-
else:
|
|
42
|
-
print(f"Could not find JSON in output")
|
|
43
|
-
return {}
|
|
44
|
-
except Exception as e:
|
|
45
|
-
print(f"Error running Skylos: {e}")
|
|
46
|
-
return {}
|
|
47
|
-
|
|
48
|
-
def run_vulture_on_file(code_file, confidence=0):
|
|
49
|
-
try:
|
|
50
|
-
cmd = ["vulture", str(code_file), f"--min-confidence={confidence}"]
|
|
51
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
52
|
-
|
|
53
|
-
items = []
|
|
54
|
-
pattern = r"([^:]+):(\d+): (\w+) '([^']+)' is never used"
|
|
55
|
-
|
|
56
|
-
for line in result.stdout.splitlines():
|
|
57
|
-
match = re.search(pattern, line)
|
|
58
|
-
if match:
|
|
59
|
-
filename, lineno, item_type, name = match.groups()
|
|
60
|
-
|
|
61
|
-
if item_type == "unused variable":
|
|
62
|
-
continue
|
|
63
|
-
elif item_type == "unused import":
|
|
64
|
-
normalized_type = "import"
|
|
65
|
-
elif item_type == "unused class":
|
|
66
|
-
normalized_type = "class"
|
|
67
|
-
elif item_type == "unused function":
|
|
68
|
-
if "." in name:
|
|
69
|
-
normalized_type = "method"
|
|
70
|
-
else:
|
|
71
|
-
normalized_type = "function"
|
|
72
|
-
elif item_type == "unused method":
|
|
73
|
-
normalized_type = "method"
|
|
74
|
-
elif item_type == "unused property":
|
|
75
|
-
normalized_type = "method"
|
|
76
|
-
else:
|
|
77
|
-
normalized_type = item_type.replace("unused ", "")
|
|
78
|
-
|
|
79
|
-
if normalized_type == "method" and "." in name:
|
|
80
|
-
items.append({
|
|
81
|
-
"name": name,
|
|
82
|
-
"type": normalized_type,
|
|
83
|
-
"file": filename,
|
|
84
|
-
"line": int(lineno)
|
|
85
|
-
})
|
|
86
|
-
else:
|
|
87
|
-
items.append({
|
|
88
|
-
"name": name,
|
|
89
|
-
"type": normalized_type,
|
|
90
|
-
"file": filename,
|
|
91
|
-
"line": int(lineno)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
return items
|
|
95
|
-
except Exception as e:
|
|
96
|
-
print(f"Error running Vulture on {code_file}: {e}")
|
|
97
|
-
return []
|
|
98
|
-
|
|
99
|
-
def normalize_item(item):
|
|
100
|
-
if isinstance(item, str):
|
|
101
|
-
return item
|
|
102
|
-
|
|
103
|
-
name = item.get("name", "")
|
|
104
|
-
|
|
105
|
-
if item.get("type") == "method" and "." in name:
|
|
106
|
-
parts = name.split(".")
|
|
107
|
-
if len(parts) > 2:
|
|
108
|
-
return ".".join(parts[-2:])
|
|
109
|
-
return name
|
|
110
|
-
|
|
111
|
-
# for other types, just get the last part
|
|
112
|
-
if "." in name:
|
|
113
|
-
return name.split(".")[-1]
|
|
114
|
-
|
|
115
|
-
return name
|
|
116
|
-
|
|
117
|
-
def get_skylos_results_as_list(skylos_results):
|
|
118
|
-
"""Convert Skylos JSON results to a flat list of items"""
|
|
119
|
-
result = []
|
|
120
|
-
|
|
121
|
-
for item in skylos_results.get("unused_functions", []):
|
|
122
|
-
# try to determine if its a method or function based on name
|
|
123
|
-
if "." in item["name"]:
|
|
124
|
-
item_type = "method"
|
|
125
|
-
else:
|
|
126
|
-
item_type = "function"
|
|
127
|
-
|
|
128
|
-
result.append({
|
|
129
|
-
"name": item["name"],
|
|
130
|
-
"type": item_type,
|
|
131
|
-
"file": item.get("file", ""),
|
|
132
|
-
"line": item.get("line", 0)
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
for item in skylos_results.get("unused_imports", []):
|
|
136
|
-
result.append({
|
|
137
|
-
"name": item["name"],
|
|
138
|
-
"type": "import",
|
|
139
|
-
"file": item.get("file", ""),
|
|
140
|
-
"line": item.get("line", 0)
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
for item in skylos_results.get("unused_classes", []):
|
|
144
|
-
result.append({
|
|
145
|
-
"name": item["name"],
|
|
146
|
-
"type": "class",
|
|
147
|
-
"file": item.get("file", ""),
|
|
148
|
-
"line": item.get("line", 0)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
return result
|
|
152
|
-
|
|
153
|
-
def find_test_cases(test_dir):
|
|
154
|
-
"""Find all test cases in the directory structure"""
|
|
155
|
-
test_cases = []
|
|
156
|
-
|
|
157
|
-
for path in Path(test_dir).rglob("ground_truth.json"):
|
|
158
|
-
code_file = path.parent / "code.py"
|
|
159
|
-
if code_file.exists():
|
|
160
|
-
test_cases.append({
|
|
161
|
-
"name": path.parent.name,
|
|
162
|
-
"code_file": code_file,
|
|
163
|
-
"ground_truth_file": path
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
return test_cases
|
|
167
|
-
|
|
168
|
-
def compare_results(skylos_results, vulture_results, ground_truth, test_case):
|
|
169
|
-
"""Compare tool results with ground truth for a single test case"""
|
|
170
|
-
gt_normalized = set()
|
|
171
|
-
for item in ground_truth:
|
|
172
|
-
if isinstance(item, dict):
|
|
173
|
-
gt_normalized.add((normalize_item(item), item.get("type")))
|
|
174
|
-
|
|
175
|
-
skylos_flat = get_skylos_results_as_list(skylos_results)
|
|
176
|
-
skylos_normalized = set()
|
|
177
|
-
for item in skylos_flat:
|
|
178
|
-
skylos_normalized.add((normalize_item(item), item.get("type")))
|
|
179
|
-
|
|
180
|
-
vulture_normalized = set()
|
|
181
|
-
for item in vulture_results:
|
|
182
|
-
vulture_normalized.add((normalize_item(item), item.get("type")))
|
|
183
|
-
|
|
184
|
-
skylos_tp = skylos_normalized.intersection(gt_normalized)
|
|
185
|
-
skylos_fp = skylos_normalized - gt_normalized
|
|
186
|
-
skylos_fn = gt_normalized - skylos_normalized
|
|
187
|
-
|
|
188
|
-
vulture_tp = vulture_normalized.intersection(gt_normalized)
|
|
189
|
-
vulture_fp = vulture_normalized - gt_normalized
|
|
190
|
-
vulture_fn = gt_normalized - vulture_normalized
|
|
191
|
-
|
|
192
|
-
vulture_only_tp = vulture_tp - skylos_tp
|
|
193
|
-
|
|
194
|
-
skylos_only_tp = skylos_tp - vulture_tp
|
|
195
|
-
|
|
196
|
-
return {
|
|
197
|
-
"name": test_case["name"],
|
|
198
|
-
"ground_truth_count": len(gt_normalized),
|
|
199
|
-
"skylos": {
|
|
200
|
-
"tp": skylos_tp,
|
|
201
|
-
"fp": skylos_fp,
|
|
202
|
-
"fn": skylos_fn,
|
|
203
|
-
"tp_count": len(skylos_tp),
|
|
204
|
-
"fp_count": len(skylos_fp),
|
|
205
|
-
"fn_count": len(skylos_fn),
|
|
206
|
-
"precision": len(skylos_tp) / len(skylos_normalized) if skylos_normalized else 0,
|
|
207
|
-
"recall": len(skylos_tp) / len(gt_normalized) if gt_normalized else 0,
|
|
208
|
-
},
|
|
209
|
-
"vulture": {
|
|
210
|
-
"tp": vulture_tp,
|
|
211
|
-
"fp": vulture_fp,
|
|
212
|
-
"fn": vulture_fn,
|
|
213
|
-
"tp_count": len(vulture_tp),
|
|
214
|
-
"fp_count": len(vulture_fp),
|
|
215
|
-
"fn_count": len(vulture_fn),
|
|
216
|
-
"precision": len(vulture_tp) / len(vulture_normalized) if vulture_normalized else 0,
|
|
217
|
-
"recall": len(vulture_tp) / len(gt_normalized) if gt_normalized else 0,
|
|
218
|
-
},
|
|
219
|
-
"vulture_only_tp": vulture_only_tp,
|
|
220
|
-
"skylos_only_tp": skylos_only_tp
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
def print_test_result(result):
|
|
224
|
-
"""Print results for a single test case"""
|
|
225
|
-
print(f"\n=== Test Case: {result['name']} ===")
|
|
226
|
-
print(f"Ground Truth: {result['ground_truth_count']} items")
|
|
227
|
-
|
|
228
|
-
print("\nSkylos Results:")
|
|
229
|
-
print(f" True Positives: {result['skylos']['tp_count']}")
|
|
230
|
-
print(f" False Positives: {result['skylos']['fp_count']}")
|
|
231
|
-
print(f" False Negatives: {result['skylos']['fn_count']}")
|
|
232
|
-
print(f" Precision: {result['skylos']['precision']:.4f}")
|
|
233
|
-
print(f" Recall: {result['skylos']['recall']:.4f}")
|
|
234
|
-
|
|
235
|
-
print("\nVulture Results:")
|
|
236
|
-
print(f" True Positives: {result['vulture']['tp_count']}")
|
|
237
|
-
print(f" False Positives: {result['vulture']['fp_count']}")
|
|
238
|
-
print(f" False Negatives: {result['vulture']['fn_count']}")
|
|
239
|
-
print(f" Precision: {result['vulture']['precision']:.4f}")
|
|
240
|
-
print(f" Recall: {result['vulture']['recall']:.4f}")
|
|
241
|
-
|
|
242
|
-
if result['skylos']['fp_count'] > 0:
|
|
243
|
-
print("\nSkylos False Positives (items Skylos flags that should be used):")
|
|
244
|
-
for item in result['skylos']['fp']:
|
|
245
|
-
print(f" - {item[0]} ({item[1]})")
|
|
246
|
-
|
|
247
|
-
if result['skylos']['fn_count'] > 0:
|
|
248
|
-
print("\nSkylos False Negatives (items Skylos misses that should be flagged):")
|
|
249
|
-
for item in result['skylos']['fn']:
|
|
250
|
-
print(f" - {item[0]} ({item[1]})")
|
|
251
|
-
|
|
252
|
-
if result['vulture_only_tp']:
|
|
253
|
-
print("\nVulture-only True Positives (What Vulture catches correctly but Skylos misses):")
|
|
254
|
-
for item in result['vulture_only_tp']:
|
|
255
|
-
print(f" - {item[0]} ({item[1]})")
|
|
256
|
-
|
|
257
|
-
if result['skylos_only_tp']:
|
|
258
|
-
print("\nSkylos-only True Positives (What Skylos catches correctly but Vulture misses):")
|
|
259
|
-
for item in result['skylos_only_tp']:
|
|
260
|
-
print(f" - {item[0]} ({item[1]})")
|
|
261
|
-
|
|
262
|
-
def main():
|
|
263
|
-
if len(sys.argv) < 3:
|
|
264
|
-
print("Usage: python analyze_test_cases.py <skylos_path> <test_cases_dir>")
|
|
265
|
-
return
|
|
266
|
-
|
|
267
|
-
skylos_path = sys.argv[1]
|
|
268
|
-
test_dir = sys.argv[2]
|
|
269
|
-
|
|
270
|
-
print(f"Finding test cases in {test_dir}...")
|
|
271
|
-
test_cases = find_test_cases(test_dir)
|
|
272
|
-
print(f"Found {len(test_cases)} test cases.")
|
|
273
|
-
|
|
274
|
-
total_results = {
|
|
275
|
-
"test_cases": len(test_cases),
|
|
276
|
-
"ground_truth_total": 0,
|
|
277
|
-
"skylos": {
|
|
278
|
-
"tp_total": 0,
|
|
279
|
-
"fp_total": 0,
|
|
280
|
-
"fn_total": 0,
|
|
281
|
-
"precision_avg": 0,
|
|
282
|
-
"recall_avg": 0
|
|
283
|
-
},
|
|
284
|
-
"vulture": {
|
|
285
|
-
"tp_total": 0,
|
|
286
|
-
"fp_total": 0,
|
|
287
|
-
"fn_total": 0,
|
|
288
|
-
"precision_avg": 0,
|
|
289
|
-
"recall_avg": 0
|
|
290
|
-
},
|
|
291
|
-
"problem_cases": []
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
test_results = []
|
|
295
|
-
for test_case in test_cases:
|
|
296
|
-
print(f"\nProcessing test case: {test_case['name']}...")
|
|
297
|
-
|
|
298
|
-
ground_truth = load_ground_truth(test_case["ground_truth_file"])
|
|
299
|
-
total_results["ground_truth_total"] += len(ground_truth)
|
|
300
|
-
|
|
301
|
-
skylos_results = run_skylos_on_file(test_case["code_file"], skylos_path)
|
|
302
|
-
|
|
303
|
-
vulture_results = run_vulture_on_file(test_case["code_file"])
|
|
304
|
-
|
|
305
|
-
result = compare_results(skylos_results, vulture_results, ground_truth, test_case)
|
|
306
|
-
test_results.append(result)
|
|
307
|
-
|
|
308
|
-
total_results["skylos"]["tp_total"] += result["skylos"]["tp_count"]
|
|
309
|
-
total_results["skylos"]["fp_total"] += result["skylos"]["fp_count"]
|
|
310
|
-
total_results["skylos"]["fn_total"] += result["skylos"]["fn_count"]
|
|
311
|
-
total_results["skylos"]["precision_avg"] += result["skylos"]["precision"]
|
|
312
|
-
total_results["skylos"]["recall_avg"] += result["skylos"]["recall"]
|
|
313
|
-
|
|
314
|
-
total_results["vulture"]["tp_total"] += result["vulture"]["tp_count"]
|
|
315
|
-
total_results["vulture"]["fp_total"] += result["vulture"]["fp_count"]
|
|
316
|
-
total_results["vulture"]["fn_total"] += result["vulture"]["fn_count"]
|
|
317
|
-
total_results["vulture"]["precision_avg"] += result["vulture"]["precision"]
|
|
318
|
-
total_results["vulture"]["recall_avg"] += result["vulture"]["recall"]
|
|
319
|
-
|
|
320
|
-
if result["skylos"]["fp_count"] > 0 or result["skylos"]["fn_count"] > 0:
|
|
321
|
-
total_results["problem_cases"].append({
|
|
322
|
-
"name": test_case["name"],
|
|
323
|
-
"fp": result["skylos"]["fp_count"],
|
|
324
|
-
"fn": result["skylos"]["fn_count"]
|
|
325
|
-
})
|
|
326
|
-
|
|
327
|
-
print_test_result(result)
|
|
328
|
-
|
|
329
|
-
if test_cases:
|
|
330
|
-
total_results["skylos"]["precision_avg"] /= len(test_cases)
|
|
331
|
-
total_results["skylos"]["recall_avg"] /= len(test_cases)
|
|
332
|
-
total_results["vulture"]["precision_avg"] /= len(test_cases)
|
|
333
|
-
total_results["vulture"]["recall_avg"] /= len(test_cases)
|
|
334
|
-
|
|
335
|
-
print("\n=== OVERALL SUMMARY ===")
|
|
336
|
-
print(f"Total Test Cases: {total_results['test_cases']}")
|
|
337
|
-
print(f"Total Ground Truth Items: {total_results['ground_truth_total']}")
|
|
338
|
-
|
|
339
|
-
print("\nSkylos Overall Results:")
|
|
340
|
-
print(f" Total True Positives: {total_results['skylos']['tp_total']}")
|
|
341
|
-
print(f" Total False Positives: {total_results['skylos']['fp_total']}")
|
|
342
|
-
print(f" Total False Negatives: {total_results['skylos']['fn_total']}")
|
|
343
|
-
print(f" Average Precision: {total_results['skylos']['precision_avg']:.4f}")
|
|
344
|
-
print(f" Average Recall: {total_results['skylos']['recall_avg']:.4f}")
|
|
345
|
-
|
|
346
|
-
print("\nVulture Overall Results:")
|
|
347
|
-
print(f" Total True Positives: {total_results['vulture']['tp_total']}")
|
|
348
|
-
print(f" Total False Positives: {total_results['vulture']['fp_total']}")
|
|
349
|
-
print(f" Total False Negatives: {total_results['vulture']['fn_total']}")
|
|
350
|
-
print(f" Average Precision: {total_results['vulture']['precision_avg']:.4f}")
|
|
351
|
-
print(f" Average Recall: {total_results['vulture']['recall_avg']:.4f}")
|
|
352
|
-
|
|
353
|
-
if total_results["problem_cases"]:
|
|
354
|
-
print("\nProblem Test Cases (with Skylos FP or FN):")
|
|
355
|
-
sorted_problems = sorted(total_results["problem_cases"],
|
|
356
|
-
key=lambda x: x["fp"] + x["fn"],
|
|
357
|
-
reverse=True)
|
|
358
|
-
for case in sorted_problems:
|
|
359
|
-
print(f" - {case['name']} (FP: {case['fp']}, FN: {case['fn']})")
|
|
360
|
-
|
|
361
|
-
print("\nRecommendation: Fix these specific test cases to improve Skylos performance.")
|
|
362
|
-
|
|
363
|
-
if __name__ == "__main__":
|
|
364
|
-
main()
|
test/sample_repo/app.py
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import argparse
|
|
4
|
-
import csv # Unused import
|
|
5
|
-
from functools import wraps
|
|
6
|
-
|
|
7
|
-
from sample_repo import User, Product
|
|
8
|
-
from sample_repo import get_current_time
|
|
9
|
-
|
|
10
|
-
def register_commands():
|
|
11
|
-
"""Register CLI commands - used function."""
|
|
12
|
-
parser = argparse.ArgumentParser(description='Sample CLI')
|
|
13
|
-
subparsers = parser.add_subparsers(dest='command')
|
|
14
|
-
|
|
15
|
-
user_parser = subparsers.add_parser('user', help='User commands')
|
|
16
|
-
user_parser.add_argument('action', choices=['list', 'create'])
|
|
17
|
-
|
|
18
|
-
product_parser = subparsers.add_parser('product', help='Product commands')
|
|
19
|
-
product_parser.add_argument('action', choices=['list'])
|
|
20
|
-
|
|
21
|
-
return parser
|
|
22
|
-
|
|
23
|
-
def handle_user_command(args):
|
|
24
|
-
"""Handle user command - used function."""
|
|
25
|
-
if args.action == 'list':
|
|
26
|
-
users = [
|
|
27
|
-
User('john', 'john@example.com'),
|
|
28
|
-
User('alice', 'alice@example.com')
|
|
29
|
-
]
|
|
30
|
-
for user in users:
|
|
31
|
-
print(f"User: {user.username} ({user.email})")
|
|
32
|
-
elif args.action == 'create':
|
|
33
|
-
user = User('new_user', 'new@example.com')
|
|
34
|
-
print(f"Created user: {user.username} ({user.email})")
|
|
35
|
-
|
|
36
|
-
def handle_product_command(args):
|
|
37
|
-
"""Handle product command - used function."""
|
|
38
|
-
if args.action == 'list':
|
|
39
|
-
products = [
|
|
40
|
-
Product('Laptop', 999.99),
|
|
41
|
-
Product('Phone', 499.99)
|
|
42
|
-
]
|
|
43
|
-
for product in products:
|
|
44
|
-
print(f"Product: {product.name} - ${product.price:.2f}")
|
|
45
|
-
|
|
46
|
-
def export_data(data, filename):
|
|
47
|
-
"""Export data to CSV - unused function."""
|
|
48
|
-
with open(filename, 'w', newline='') as f:
|
|
49
|
-
writer = csv.writer(f)
|
|
50
|
-
writer.writerow(['id', 'name', 'value'])
|
|
51
|
-
for item in data:
|
|
52
|
-
writer.writerow([item['id'], item['name'], item['value']])
|
|
53
|
-
|
|
54
|
-
def import_data(filename):
|
|
55
|
-
"""Import data from CSV - unused function."""
|
|
56
|
-
data = []
|
|
57
|
-
with open(filename, 'r', newline='') as f:
|
|
58
|
-
reader = csv.reader(f)
|
|
59
|
-
next(reader)
|
|
60
|
-
for row in reader:
|
|
61
|
-
data.append({
|
|
62
|
-
'id': row[0],
|
|
63
|
-
'name': row[1],
|
|
64
|
-
'value': row[2]
|
|
65
|
-
})
|
|
66
|
-
return data
|
|
67
|
-
|
|
68
|
-
class CommandDecorator:
|
|
69
|
-
"""Decorator for CLI commands - unused class."""
|
|
70
|
-
|
|
71
|
-
def __init__(self, name, help_text):
|
|
72
|
-
self.name = name
|
|
73
|
-
self.help_text = help_text
|
|
74
|
-
|
|
75
|
-
def __call__(self, func):
|
|
76
|
-
"""Call method - unused method."""
|
|
77
|
-
@wraps(func)
|
|
78
|
-
def wrapper(*args, **kwargs):
|
|
79
|
-
print(f"Executing command: {self.name}")
|
|
80
|
-
return func(*args, **kwargs)
|
|
81
|
-
return wrapper
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import uuid
|
|
3
|
-
import hashlib # Unused import
|
|
4
|
-
from typing import List, Dict, Optional
|
|
5
|
-
|
|
6
|
-
from .utils import get_current_time
|
|
7
|
-
|
|
8
|
-
class BaseModel:
|
|
9
|
-
"""Base model class with common functionality."""
|
|
10
|
-
|
|
11
|
-
def __init__(self, id=None):
|
|
12
|
-
self.id = id or str(uuid.uuid4())
|
|
13
|
-
self.created_at = get_current_time()
|
|
14
|
-
|
|
15
|
-
def to_dict(self):
|
|
16
|
-
"""Convert model to dictionary - used method."""
|
|
17
|
-
return {
|
|
18
|
-
'id': self.id,
|
|
19
|
-
'created_at': self.created_at
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
def to_json(self):
|
|
23
|
-
"""Convert model to JSON - unused method."""
|
|
24
|
-
import json
|
|
25
|
-
return json.dumps(self.to_dict())
|
|
26
|
-
|
|
27
|
-
@classmethod
|
|
28
|
-
def from_dict(cls, data):
|
|
29
|
-
"""Create instance from dictionary - used method."""
|
|
30
|
-
instance = cls(id=data.get('id'))
|
|
31
|
-
if 'created_at' in data:
|
|
32
|
-
instance.created_at = data['created_at']
|
|
33
|
-
return instance
|
|
34
|
-
|
|
35
|
-
class User(BaseModel):
|
|
36
|
-
"""User model - used class."""
|
|
37
|
-
|
|
38
|
-
def __init__(self, username, email, id=None):
|
|
39
|
-
super().__init__(id)
|
|
40
|
-
self.username = username
|
|
41
|
-
self.email = email
|
|
42
|
-
self.active = True
|
|
43
|
-
|
|
44
|
-
def to_dict(self):
|
|
45
|
-
"""Override to_dict to include user fields - used method."""
|
|
46
|
-
data = super().to_dict()
|
|
47
|
-
data.update({
|
|
48
|
-
'username': self.username,
|
|
49
|
-
'email': self.email,
|
|
50
|
-
'active': self.active
|
|
51
|
-
})
|
|
52
|
-
return data
|
|
53
|
-
|
|
54
|
-
def deactivate(self):
|
|
55
|
-
"""Deactivate user - used method."""
|
|
56
|
-
self.active = False
|
|
57
|
-
|
|
58
|
-
def send_welcome_email(self):
|
|
59
|
-
"""Send welcome email - unused method."""
|
|
60
|
-
pass
|
|
61
|
-
|
|
62
|
-
class Product(BaseModel):
|
|
63
|
-
"""Product model - used class."""
|
|
64
|
-
|
|
65
|
-
def __init__(self, name, price, id=None):
|
|
66
|
-
super().__init__(id)
|
|
67
|
-
self.name = name
|
|
68
|
-
self.price = price
|
|
69
|
-
|
|
70
|
-
def to_dict(self):
|
|
71
|
-
"""Override to_dict for product - used method."""
|
|
72
|
-
data = super().to_dict()
|
|
73
|
-
data.update({
|
|
74
|
-
'name': self.name,
|
|
75
|
-
'price': self.price
|
|
76
|
-
})
|
|
77
|
-
return data
|
|
78
|
-
|
|
79
|
-
def apply_discount(self, percentage):
|
|
80
|
-
"""Apply discount to product - unused method."""
|
|
81
|
-
self.price = self.price * (1 - percentage / 100)
|
|
82
|
-
|
|
83
|
-
class Order(BaseModel):
|
|
84
|
-
"""Order model - unused class."""
|
|
85
|
-
|
|
86
|
-
def __init__(self, user_id, products, id=None):
|
|
87
|
-
super().__init__(id)
|
|
88
|
-
self.user_id = user_id
|
|
89
|
-
self.products = products
|
|
90
|
-
self.status = 'pending'
|
|
91
|
-
|
|
92
|
-
def to_dict(self):
|
|
93
|
-
"""Order to dict - unused method."""
|
|
94
|
-
data = super().to_dict()
|
|
95
|
-
data.update({
|
|
96
|
-
'user_id': self.user_id,
|
|
97
|
-
'products': self.products,
|
|
98
|
-
'status': self.status
|
|
99
|
-
})
|
|
100
|
-
return data
|
|
101
|
-
|
|
102
|
-
def complete(self):
|
|
103
|
-
"""Mark order as complete - unused method."""
|
|
104
|
-
self.status = 'completed'
|
|
105
|
-
|
|
106
|
-
class Cart:
|
|
107
|
-
"""Shopping cart - unused class."""
|
|
108
|
-
|
|
109
|
-
def __init__(self, user_id):
|
|
110
|
-
self.user_id = user_id
|
|
111
|
-
self.items = []
|
|
112
|
-
|
|
113
|
-
def add_item(self, product_id, quantity=1):
|
|
114
|
-
"""Add item to cart - unused method."""
|
|
115
|
-
self.items.append({
|
|
116
|
-
'product_id': product_id,
|
|
117
|
-
'quantity': quantity
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
def clear(self):
|
|
121
|
-
"""Clear cart - unused method."""
|
|
122
|
-
self.items = []
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
import logging
|
|
4
|
-
from functools import wraps
|
|
5
|
-
|
|
6
|
-
from sample_repo import User, Product
|
|
7
|
-
from sample_repo import format_currency
|
|
8
|
-
|
|
9
|
-
def require_auth(f):
|
|
10
|
-
"""Decorator to require authentication - used decorator."""
|
|
11
|
-
@wraps(f)
|
|
12
|
-
def decorated(*args, **kwargs):
|
|
13
|
-
return f(*args, **kwargs)
|
|
14
|
-
return decorated
|
|
15
|
-
|
|
16
|
-
def rate_limit(limit):
|
|
17
|
-
"""Decorator for rate limiting - unused decorator."""
|
|
18
|
-
def decorator(f):
|
|
19
|
-
@wraps(f)
|
|
20
|
-
def decorated(*args, **kwargs):
|
|
21
|
-
return f(*args, **kwargs)
|
|
22
|
-
return decorated
|
|
23
|
-
return decorator
|
|
24
|
-
|
|
25
|
-
class App:
|
|
26
|
-
"""Simple application class - used class."""
|
|
27
|
-
|
|
28
|
-
def __init__(self):
|
|
29
|
-
self.routes = {}
|
|
30
|
-
self.middlewares = []
|
|
31
|
-
self._setup_routes()
|
|
32
|
-
|
|
33
|
-
def _setup_routes(self):
|
|
34
|
-
"""Set up API routes - used method."""
|
|
35
|
-
self.routes = {
|
|
36
|
-
'/users': self.get_users,
|
|
37
|
-
'/users/create': self.create_user,
|
|
38
|
-
'/products': self.get_products
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
def add_middleware(self, middleware):
|
|
42
|
-
"""Add middleware - unused method."""
|
|
43
|
-
self.middlewares.append(middleware)
|
|
44
|
-
|
|
45
|
-
@require_auth
|
|
46
|
-
def get_users(self):
|
|
47
|
-
"""Get users endpoint - used method."""
|
|
48
|
-
users = [
|
|
49
|
-
User('john', 'john@example.com'),
|
|
50
|
-
User('alice', 'alice@example.com')
|
|
51
|
-
]
|
|
52
|
-
return [user.to_dict() for user in users]
|
|
53
|
-
|
|
54
|
-
def create_user(self):
|
|
55
|
-
"""Create user endpoint - used method."""
|
|
56
|
-
user = User('new_user', 'new@example.com')
|
|
57
|
-
return user.to_dict()
|
|
58
|
-
|
|
59
|
-
def get_products(self):
|
|
60
|
-
"""Get products endpoint - used method."""
|
|
61
|
-
products = [
|
|
62
|
-
Product('Laptop', 999.99),
|
|
63
|
-
Product('Phone', 499.99)
|
|
64
|
-
]
|
|
65
|
-
return [
|
|
66
|
-
{**product.to_dict(), 'formatted_price': format_currency(product.price)}
|
|
67
|
-
for product in products
|
|
68
|
-
]
|
|
69
|
-
|
|
70
|
-
@rate_limit(100)
|
|
71
|
-
def get_analytics(self):
|
|
72
|
-
"""Get analytics endpoint - unused method."""
|
|
73
|
-
return {'visits': 1000, 'unique_users': 500}
|
|
74
|
-
|
|
75
|
-
def run(self):
|
|
76
|
-
"""Run the application - used method."""
|
|
77
|
-
print("API server running...")
|
|
78
|
-
|
|
79
|
-
def create_app():
|
|
80
|
-
"""Create application - used function."""
|
|
81
|
-
return App()
|
|
82
|
-
|
|
83
|
-
def parse_config(config_path):
|
|
84
|
-
"""Parse configuration file - unused function."""
|
|
85
|
-
if not os.path.exists(config_path):
|
|
86
|
-
return {}
|
|
87
|
-
|
|
88
|
-
with open(config_path, 'r') as f:
|
|
89
|
-
return json.load(f)
|