auto-coder 0.1.313__py3-none-any.whl → 0.1.315__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 auto-coder might be problematic. Click here for more details.
- {auto_coder-0.1.313.dist-info → auto_coder-0.1.315.dist-info}/METADATA +1 -1
- {auto_coder-0.1.313.dist-info → auto_coder-0.1.315.dist-info}/RECORD +13 -8
- autocoder/auto_coder_runner.py +22 -26
- autocoder/linters/__init__.py +0 -0
- autocoder/linters/base_linter.py +108 -0
- autocoder/linters/code_linter.py +588 -0
- autocoder/linters/linter_factory.py +243 -0
- autocoder/linters/python_linter.py +616 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.313.dist-info → auto_coder-0.1.315.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.313.dist-info → auto_coder-0.1.315.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.313.dist-info → auto_coder-0.1.315.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.313.dist-info → auto_coder-0.1.315.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module for linting Python code.
|
|
3
|
+
This module provides functionality to analyze Python code for quality and best practices.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import json
|
|
9
|
+
import subprocess
|
|
10
|
+
import tempfile
|
|
11
|
+
from typing import Dict, List, Any, Optional, Tuple
|
|
12
|
+
|
|
13
|
+
from autocoder.linters.base_linter import BaseLinter
|
|
14
|
+
|
|
15
|
+
class PythonLinter(BaseLinter):
|
|
16
|
+
"""
|
|
17
|
+
A class that provides linting functionality for Python code.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, verbose: bool = False):
|
|
21
|
+
"""
|
|
22
|
+
Initialize the PythonLinter.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
verbose (bool): Whether to display verbose output.
|
|
26
|
+
"""
|
|
27
|
+
super().__init__(verbose)
|
|
28
|
+
|
|
29
|
+
def get_supported_extensions(self) -> List[str]:
|
|
30
|
+
"""
|
|
31
|
+
Get the list of file extensions supported by this linter.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List[str]: List of supported file extensions.
|
|
35
|
+
"""
|
|
36
|
+
return ['.py']
|
|
37
|
+
|
|
38
|
+
def _check_dependencies(self) -> bool:
|
|
39
|
+
"""
|
|
40
|
+
Check if required dependencies (pylint, flake8, black) are installed.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
bool: True if all dependencies are available, False otherwise.
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
# Check if python is installed
|
|
47
|
+
subprocess.run([sys.executable, "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
48
|
+
|
|
49
|
+
# Check for pylint or flake8
|
|
50
|
+
has_pylint = False
|
|
51
|
+
has_flake8 = False
|
|
52
|
+
has_black = False
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
subprocess.run([sys.executable, "-m", "pylint", "--version"],
|
|
56
|
+
check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
57
|
+
has_pylint = True
|
|
58
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
59
|
+
if self.verbose:
|
|
60
|
+
print("Pylint not found.")
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
subprocess.run([sys.executable, "-m", "flake8", "--version"],
|
|
64
|
+
check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
65
|
+
has_flake8 = True
|
|
66
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
67
|
+
if self.verbose:
|
|
68
|
+
print("Flake8 not found.")
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
subprocess.run([sys.executable, "-m", "black", "--version"],
|
|
72
|
+
check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
73
|
+
has_black = True
|
|
74
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
75
|
+
if self.verbose:
|
|
76
|
+
print("Black not found.")
|
|
77
|
+
|
|
78
|
+
# Need at least one linter
|
|
79
|
+
return has_pylint or has_flake8 or has_black
|
|
80
|
+
|
|
81
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
def _install_dependencies_if_needed(self) -> bool:
|
|
85
|
+
"""
|
|
86
|
+
Install required linting tools if they are not already installed.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
bool: True if installation was successful or dependencies already exist, False otherwise.
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
# Check for each dependency separately
|
|
93
|
+
dependencies_to_install = []
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
subprocess.run([sys.executable, "-m", "pylint", "--version"],
|
|
97
|
+
check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
98
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
99
|
+
dependencies_to_install.append("pylint")
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
subprocess.run([sys.executable, "-m", "flake8", "--version"],
|
|
103
|
+
check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
104
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
105
|
+
dependencies_to_install.append("flake8")
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
subprocess.run([sys.executable, "-m", "black", "--version"],
|
|
109
|
+
check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
110
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
111
|
+
dependencies_to_install.append("black")
|
|
112
|
+
|
|
113
|
+
# Install missing dependencies
|
|
114
|
+
if dependencies_to_install:
|
|
115
|
+
if self.verbose:
|
|
116
|
+
print(f"Installing missing dependencies: {', '.join(dependencies_to_install)}")
|
|
117
|
+
|
|
118
|
+
install_cmd = [sys.executable, "-m", "pip", "install"] + dependencies_to_install
|
|
119
|
+
|
|
120
|
+
result = subprocess.run(
|
|
121
|
+
install_cmd,
|
|
122
|
+
stdout=subprocess.PIPE if not self.verbose else None,
|
|
123
|
+
stderr=subprocess.PIPE if not self.verbose else None
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return result.returncode == 0
|
|
127
|
+
|
|
128
|
+
return True # All dependencies already installed
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
if self.verbose:
|
|
132
|
+
print(f"Error installing dependencies: {str(e)}")
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def _run_pylint(self, target: str) -> Dict[str, Any]:
|
|
136
|
+
"""
|
|
137
|
+
Run pylint on the target file or directory.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
target (str): Path to the file or directory to lint.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Dict[str, Any]: The pylint results.
|
|
144
|
+
"""
|
|
145
|
+
result = {
|
|
146
|
+
'error_count': 0,
|
|
147
|
+
'warning_count': 0,
|
|
148
|
+
'issues': []
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
# Create a temp file to store JSON output
|
|
153
|
+
with tempfile.NamedTemporaryFile(suffix='.json', delete=False) as tmp:
|
|
154
|
+
tmp_path = tmp.name
|
|
155
|
+
|
|
156
|
+
# Run pylint with JSON reporter
|
|
157
|
+
cmd = [
|
|
158
|
+
sys.executable,
|
|
159
|
+
"-m",
|
|
160
|
+
"pylint",
|
|
161
|
+
"--output-format=json",
|
|
162
|
+
target
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
process = subprocess.run(
|
|
166
|
+
cmd,
|
|
167
|
+
stdout=subprocess.PIPE,
|
|
168
|
+
stderr=subprocess.PIPE,
|
|
169
|
+
text=True
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if process.stdout:
|
|
173
|
+
try:
|
|
174
|
+
pylint_output = json.loads(process.stdout)
|
|
175
|
+
|
|
176
|
+
# Process pylint issues
|
|
177
|
+
for message in pylint_output:
|
|
178
|
+
severity = "warning"
|
|
179
|
+
if message.get('type') in ['error', 'fatal']:
|
|
180
|
+
severity = "error"
|
|
181
|
+
result['error_count'] += 1
|
|
182
|
+
else:
|
|
183
|
+
result['warning_count'] += 1
|
|
184
|
+
|
|
185
|
+
issue = {
|
|
186
|
+
'file': message.get('path', ''),
|
|
187
|
+
'line': message.get('line', 0),
|
|
188
|
+
'column': message.get('column', 0),
|
|
189
|
+
'severity': severity,
|
|
190
|
+
'message': message.get('message', ''),
|
|
191
|
+
'rule': message.get('symbol', 'unknown'),
|
|
192
|
+
'tool': 'pylint'
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
result['issues'].append(issue)
|
|
196
|
+
|
|
197
|
+
except json.JSONDecodeError:
|
|
198
|
+
# Handle non-JSON output (like when no files to check)
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
# Cleanup temp file
|
|
202
|
+
try:
|
|
203
|
+
os.unlink(tmp_path)
|
|
204
|
+
except:
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
except Exception as e:
|
|
208
|
+
if self.verbose:
|
|
209
|
+
print(f"Error running pylint: {str(e)}")
|
|
210
|
+
|
|
211
|
+
return result
|
|
212
|
+
|
|
213
|
+
def _run_flake8(self, target: str) -> Dict[str, Any]:
|
|
214
|
+
"""
|
|
215
|
+
Run flake8 on the target file or directory.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
target (str): Path to the file or directory to lint.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Dict[str, Any]: The flake8 results.
|
|
222
|
+
"""
|
|
223
|
+
result = {
|
|
224
|
+
'error_count': 0,
|
|
225
|
+
'warning_count': 0,
|
|
226
|
+
'issues': []
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
# Run flake8
|
|
231
|
+
cmd = [
|
|
232
|
+
sys.executable,
|
|
233
|
+
"-m",
|
|
234
|
+
"flake8",
|
|
235
|
+
target
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
process = subprocess.run(
|
|
239
|
+
cmd,
|
|
240
|
+
stdout=subprocess.PIPE,
|
|
241
|
+
stderr=subprocess.PIPE,
|
|
242
|
+
text=True
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
if process.stdout:
|
|
246
|
+
# Parse flake8 output
|
|
247
|
+
for line in process.stdout.splitlines():
|
|
248
|
+
if not line.strip():
|
|
249
|
+
continue
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
# Parse the line (format: "file:line:col: code message")
|
|
253
|
+
parts = line.split(':', 3)
|
|
254
|
+
if len(parts) >= 4:
|
|
255
|
+
file_path = parts[0]
|
|
256
|
+
line_num = int(parts[1])
|
|
257
|
+
col_num = int(parts[2].split(' ')[0])
|
|
258
|
+
|
|
259
|
+
code_message = parts[3].strip()
|
|
260
|
+
code_parts = code_message.split(' ', 1)
|
|
261
|
+
|
|
262
|
+
if len(code_parts) >= 2:
|
|
263
|
+
code = code_parts[0]
|
|
264
|
+
message = code_parts[1]
|
|
265
|
+
else:
|
|
266
|
+
code = "unknown"
|
|
267
|
+
message = code_message
|
|
268
|
+
|
|
269
|
+
# Determine severity based on error code
|
|
270
|
+
severity = "warning"
|
|
271
|
+
# E errors are generally more serious than F warnings
|
|
272
|
+
if code.startswith('E'):
|
|
273
|
+
severity = "error"
|
|
274
|
+
result['error_count'] += 1
|
|
275
|
+
else:
|
|
276
|
+
result['warning_count'] += 1
|
|
277
|
+
|
|
278
|
+
issue = {
|
|
279
|
+
'file': file_path,
|
|
280
|
+
'line': line_num,
|
|
281
|
+
'column': col_num,
|
|
282
|
+
'severity': severity,
|
|
283
|
+
'message': message,
|
|
284
|
+
'rule': code,
|
|
285
|
+
'tool': 'flake8'
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
result['issues'].append(issue)
|
|
289
|
+
except (ValueError, IndexError):
|
|
290
|
+
# Skip malformed lines
|
|
291
|
+
continue
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
if self.verbose:
|
|
295
|
+
print(f"Error running flake8: {str(e)}")
|
|
296
|
+
|
|
297
|
+
return result
|
|
298
|
+
|
|
299
|
+
def _run_black(self, target: str, fix: bool) -> Dict[str, Any]:
|
|
300
|
+
"""
|
|
301
|
+
Run black on the target file or directory to check formatting or fix it.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
target (str): Path to the file or directory to format.
|
|
305
|
+
fix (bool): Whether to automatically fix formatting issues.
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
Dict[str, Any]: The black results.
|
|
309
|
+
"""
|
|
310
|
+
result = {
|
|
311
|
+
'error_count': 0,
|
|
312
|
+
'warning_count': 0,
|
|
313
|
+
'issues': []
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
# Build command
|
|
318
|
+
cmd = [
|
|
319
|
+
sys.executable,
|
|
320
|
+
"-m",
|
|
321
|
+
"black",
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
# Check-only mode if not fixing
|
|
325
|
+
if not fix:
|
|
326
|
+
cmd.append("--check")
|
|
327
|
+
|
|
328
|
+
# Add target
|
|
329
|
+
cmd.append(target)
|
|
330
|
+
|
|
331
|
+
process = subprocess.run(
|
|
332
|
+
cmd,
|
|
333
|
+
stdout=subprocess.PIPE,
|
|
334
|
+
stderr=subprocess.PIPE,
|
|
335
|
+
text=True
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Black exit code is 0 if no changes, 1 if changes were needed
|
|
339
|
+
if process.returncode == 1 and not fix:
|
|
340
|
+
# Parse output to find which files would be reformatted
|
|
341
|
+
for line in process.stdout.splitlines():
|
|
342
|
+
if line.startswith("would reformat"):
|
|
343
|
+
file_path = line.replace("would reformat ", "").strip()
|
|
344
|
+
|
|
345
|
+
result['warning_count'] += 1
|
|
346
|
+
|
|
347
|
+
issue = {
|
|
348
|
+
'file': file_path,
|
|
349
|
+
'line': 0, # Black doesn't provide line numbers
|
|
350
|
+
'column': 0,
|
|
351
|
+
'severity': "warning",
|
|
352
|
+
'message': "Code formatting doesn't match Black style",
|
|
353
|
+
'rule': "formatting",
|
|
354
|
+
'tool': 'black'
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
result['issues'].append(issue)
|
|
358
|
+
|
|
359
|
+
# If auto-fixing and Black reports changes
|
|
360
|
+
if fix and process.returncode == 0 and "reformatted" in process.stderr:
|
|
361
|
+
# This is good - it means Black fixed some issues
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
except Exception as e:
|
|
365
|
+
if self.verbose:
|
|
366
|
+
print(f"Error running black: {str(e)}")
|
|
367
|
+
|
|
368
|
+
return result
|
|
369
|
+
|
|
370
|
+
def lint_file(self, file_path: str, fix: bool = False) -> Dict[str, Any]:
|
|
371
|
+
"""
|
|
372
|
+
Lint a single Python file.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
file_path (str): Path to the file to lint.
|
|
376
|
+
fix (bool): Whether to automatically fix fixable issues.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
Dict[str, Any]: Lint results.
|
|
380
|
+
"""
|
|
381
|
+
result = {
|
|
382
|
+
'success': False,
|
|
383
|
+
'language': 'python',
|
|
384
|
+
'files_analyzed': 1,
|
|
385
|
+
'error_count': 0,
|
|
386
|
+
'warning_count': 0,
|
|
387
|
+
'issues': []
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# Check if file exists
|
|
391
|
+
if not os.path.exists(file_path) or not os.path.isfile(file_path):
|
|
392
|
+
result['error'] = f"File '{file_path}' does not exist or is not a file"
|
|
393
|
+
return result
|
|
394
|
+
|
|
395
|
+
# Check if file is supported
|
|
396
|
+
if not self.is_supported_file(file_path):
|
|
397
|
+
result['error'] = f"Unsupported file type for '{file_path}'"
|
|
398
|
+
return result
|
|
399
|
+
|
|
400
|
+
# Check dependencies
|
|
401
|
+
if not self._check_dependencies():
|
|
402
|
+
# Try to install dependencies
|
|
403
|
+
if not self._install_dependencies_if_needed():
|
|
404
|
+
result['error'] = "Required dependencies are not installed and could not be installed automatically"
|
|
405
|
+
return result
|
|
406
|
+
|
|
407
|
+
# Run black first (to format if fix=True)
|
|
408
|
+
try:
|
|
409
|
+
black_result = self._run_black(file_path, fix)
|
|
410
|
+
result['issues'].extend(black_result['issues'])
|
|
411
|
+
result['error_count'] += black_result['error_count']
|
|
412
|
+
result['warning_count'] += black_result['warning_count']
|
|
413
|
+
except Exception as e:
|
|
414
|
+
if self.verbose:
|
|
415
|
+
print(f"Error running black: {str(e)}")
|
|
416
|
+
|
|
417
|
+
# Run pylint
|
|
418
|
+
try:
|
|
419
|
+
pylint_result = self._run_pylint(file_path)
|
|
420
|
+
result['issues'].extend(pylint_result['issues'])
|
|
421
|
+
result['error_count'] += pylint_result['error_count']
|
|
422
|
+
result['warning_count'] += pylint_result['warning_count']
|
|
423
|
+
except Exception as e:
|
|
424
|
+
if self.verbose:
|
|
425
|
+
print(f"Error running pylint: {str(e)}")
|
|
426
|
+
|
|
427
|
+
# Run flake8
|
|
428
|
+
try:
|
|
429
|
+
flake8_result = self._run_flake8(file_path)
|
|
430
|
+
result['issues'].extend(flake8_result['issues'])
|
|
431
|
+
result['error_count'] += flake8_result['error_count']
|
|
432
|
+
result['warning_count'] += flake8_result['warning_count']
|
|
433
|
+
except Exception as e:
|
|
434
|
+
if self.verbose:
|
|
435
|
+
print(f"Error running flake8: {str(e)}")
|
|
436
|
+
|
|
437
|
+
# Mark as successful
|
|
438
|
+
result['success'] = True
|
|
439
|
+
|
|
440
|
+
return result
|
|
441
|
+
|
|
442
|
+
def lint_project(self, project_path: str, fix: bool = False) -> Dict[str, Any]:
|
|
443
|
+
"""
|
|
444
|
+
Lint a Python project.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
project_path (str): Path to the project directory.
|
|
448
|
+
fix (bool): Whether to automatically fix fixable issues.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
Dict[str, Any]: Lint results.
|
|
452
|
+
"""
|
|
453
|
+
result = {
|
|
454
|
+
'success': False,
|
|
455
|
+
'language': 'python',
|
|
456
|
+
'files_analyzed': 0,
|
|
457
|
+
'error_count': 0,
|
|
458
|
+
'warning_count': 0,
|
|
459
|
+
'issues': []
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
# Check if the path exists
|
|
463
|
+
if not os.path.exists(project_path) or not os.path.isdir(project_path):
|
|
464
|
+
result['error'] = f"Path '{project_path}' does not exist or is not a directory"
|
|
465
|
+
return result
|
|
466
|
+
|
|
467
|
+
# Check dependencies
|
|
468
|
+
if not self._check_dependencies():
|
|
469
|
+
# Try to install dependencies
|
|
470
|
+
if not self._install_dependencies_if_needed():
|
|
471
|
+
result['error'] = "Required dependencies are not installed and could not be installed automatically"
|
|
472
|
+
return result
|
|
473
|
+
|
|
474
|
+
# Find Python files in the project
|
|
475
|
+
python_files = []
|
|
476
|
+
for root, _, files in os.walk(project_path):
|
|
477
|
+
for file in files:
|
|
478
|
+
if file.endswith('.py'):
|
|
479
|
+
python_files.append(os.path.join(root, file))
|
|
480
|
+
|
|
481
|
+
result['files_analyzed'] = len(python_files)
|
|
482
|
+
|
|
483
|
+
# Run black first (to format if fix=True)
|
|
484
|
+
try:
|
|
485
|
+
black_result = self._run_black(project_path, fix)
|
|
486
|
+
result['issues'].extend(black_result['issues'])
|
|
487
|
+
result['error_count'] += black_result['error_count']
|
|
488
|
+
result['warning_count'] += black_result['warning_count']
|
|
489
|
+
except Exception as e:
|
|
490
|
+
if self.verbose:
|
|
491
|
+
print(f"Error running black: {str(e)}")
|
|
492
|
+
|
|
493
|
+
# Run pylint
|
|
494
|
+
try:
|
|
495
|
+
pylint_result = self._run_pylint(project_path)
|
|
496
|
+
result['issues'].extend(pylint_result['issues'])
|
|
497
|
+
result['error_count'] += pylint_result['error_count']
|
|
498
|
+
result['warning_count'] += pylint_result['warning_count']
|
|
499
|
+
except Exception as e:
|
|
500
|
+
if self.verbose:
|
|
501
|
+
print(f"Error running pylint: {str(e)}")
|
|
502
|
+
|
|
503
|
+
# Run flake8
|
|
504
|
+
try:
|
|
505
|
+
flake8_result = self._run_flake8(project_path)
|
|
506
|
+
result['issues'].extend(flake8_result['issues'])
|
|
507
|
+
result['error_count'] += flake8_result['error_count']
|
|
508
|
+
result['warning_count'] += flake8_result['warning_count']
|
|
509
|
+
except Exception as e:
|
|
510
|
+
if self.verbose:
|
|
511
|
+
print(f"Error running flake8: {str(e)}")
|
|
512
|
+
|
|
513
|
+
# Mark as successful
|
|
514
|
+
result['success'] = True
|
|
515
|
+
|
|
516
|
+
return result
|
|
517
|
+
|
|
518
|
+
def format_lint_result(self, lint_result: Dict[str, Any]) -> str:
|
|
519
|
+
"""
|
|
520
|
+
Format lint results into a human-readable string.
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
lint_result (Dict): The lint result dictionary.
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
str: A formatted string representation of the lint results.
|
|
527
|
+
"""
|
|
528
|
+
if not lint_result.get('success', False):
|
|
529
|
+
return f"Linting failed: {lint_result.get('error', 'Unknown error')}"
|
|
530
|
+
|
|
531
|
+
header = "Python Code Lint Results"
|
|
532
|
+
files_analyzed = lint_result.get('files_analyzed', 0)
|
|
533
|
+
error_count = lint_result.get('error_count', 0)
|
|
534
|
+
warning_count = lint_result.get('warning_count', 0)
|
|
535
|
+
|
|
536
|
+
output = [
|
|
537
|
+
header,
|
|
538
|
+
f"{'=' * 30}",
|
|
539
|
+
f"Files analyzed: {files_analyzed}",
|
|
540
|
+
f"Errors: {error_count}",
|
|
541
|
+
f"Warnings: {warning_count}",
|
|
542
|
+
""
|
|
543
|
+
]
|
|
544
|
+
|
|
545
|
+
if error_count == 0 and warning_count == 0:
|
|
546
|
+
output.append("No issues found. Great job!")
|
|
547
|
+
else:
|
|
548
|
+
output.append("Issues:")
|
|
549
|
+
output.append(f"{'-' * 30}")
|
|
550
|
+
|
|
551
|
+
# Group issues by file
|
|
552
|
+
issues_by_file = {}
|
|
553
|
+
for issue in lint_result.get('issues', []):
|
|
554
|
+
file_path = issue.get('file', '')
|
|
555
|
+
if file_path not in issues_by_file:
|
|
556
|
+
issues_by_file[file_path] = []
|
|
557
|
+
issues_by_file[file_path].append(issue)
|
|
558
|
+
|
|
559
|
+
# Display issues grouped by file
|
|
560
|
+
for file_path, issues in issues_by_file.items():
|
|
561
|
+
output.append(f"\nFile: {file_path}")
|
|
562
|
+
|
|
563
|
+
for issue in issues:
|
|
564
|
+
severity = issue.get('severity', '').upper()
|
|
565
|
+
line = issue.get('line', 0)
|
|
566
|
+
column = issue.get('column', 0)
|
|
567
|
+
message = issue.get('message', '')
|
|
568
|
+
rule = issue.get('rule', 'unknown')
|
|
569
|
+
tool = issue.get('tool', 'unknown')
|
|
570
|
+
|
|
571
|
+
output.append(f" [{severity}] Line {line}, Col {column}: {message} ({rule} - {tool})")
|
|
572
|
+
|
|
573
|
+
return "\n".join(output)
|
|
574
|
+
|
|
575
|
+
def lint_python_file(file_path: str, fix: bool = False, verbose: bool = False) -> Dict[str, Any]:
|
|
576
|
+
"""
|
|
577
|
+
Utility function to lint a single Python file.
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
file_path (str): Path to the file to lint.
|
|
581
|
+
fix (bool): Whether to automatically fix fixable issues.
|
|
582
|
+
verbose (bool): Whether to display verbose output.
|
|
583
|
+
|
|
584
|
+
Returns:
|
|
585
|
+
Dict[str, Any]: A dictionary containing lint results.
|
|
586
|
+
"""
|
|
587
|
+
linter = PythonLinter(verbose=verbose)
|
|
588
|
+
return linter.lint_file(file_path, fix=fix)
|
|
589
|
+
|
|
590
|
+
def lint_python_project(project_path: str, fix: bool = False, verbose: bool = False) -> Dict[str, Any]:
|
|
591
|
+
"""
|
|
592
|
+
Utility function to lint a Python project.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
project_path (str): Path to the project directory.
|
|
596
|
+
fix (bool): Whether to automatically fix fixable issues.
|
|
597
|
+
verbose (bool): Whether to display verbose output.
|
|
598
|
+
|
|
599
|
+
Returns:
|
|
600
|
+
Dict[str, Any]: A dictionary containing lint results.
|
|
601
|
+
"""
|
|
602
|
+
linter = PythonLinter(verbose=verbose)
|
|
603
|
+
return linter.lint_project(project_path, fix=fix)
|
|
604
|
+
|
|
605
|
+
def format_lint_result(lint_result: Dict[str, Any]) -> str:
|
|
606
|
+
"""
|
|
607
|
+
Format lint results into a human-readable string.
|
|
608
|
+
|
|
609
|
+
Args:
|
|
610
|
+
lint_result (Dict): The lint result dictionary.
|
|
611
|
+
|
|
612
|
+
Returns:
|
|
613
|
+
str: A formatted string representation of the lint results.
|
|
614
|
+
"""
|
|
615
|
+
linter = PythonLinter()
|
|
616
|
+
return linter.format_lint_result(lint_result)
|
autocoder/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.315"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|