0
# Exception Handling
1
2
Comprehensive exception hierarchy for handling errors during flake8 execution, including plugin loading failures, execution errors, and early termination scenarios.
3
4
## Capabilities
5
6
### Base Exception Class
7
8
Base exception class for all flake8-specific errors.
9
10
```python { .api }
11
class Flake8Exception(Exception):
12
"""
13
Base exception class for all flake8-specific errors.
14
15
All flake8-related exceptions inherit from this class, allowing
16
for easy exception handling of flake8-specific issues.
17
18
Example:
19
try:
20
# flake8 operations
21
except Flake8Exception as e:
22
# Handle any flake8-specific error
23
"""
24
```
25
26
### Execution Control Exceptions
27
28
Exceptions related to execution flow and early termination.
29
30
```python { .api }
31
class EarlyQuit(Flake8Exception):
32
"""
33
Exception raised when flake8 encounters a KeyboardInterrupt.
34
35
This exception is raised when the user interrupts flake8 execution
36
(typically with Ctrl+C) to provide clean termination handling.
37
38
Example:
39
try:
40
style_guide.check_files(large_file_list)
41
except EarlyQuit:
42
print("Flake8 execution was interrupted by user")
43
"""
44
45
class ExecutionError(Flake8Exception):
46
"""
47
Exception raised during general execution of flake8.
48
49
This exception covers various execution errors that can occur
50
during flake8's operation that are not covered by more specific
51
exception types.
52
53
Example:
54
try:
55
app.run(invalid_args)
56
except ExecutionError as e:
57
print(f"Execution failed: {e}")
58
"""
59
```
60
61
### Plugin-Related Exceptions
62
63
Exceptions specifically related to plugin loading and execution failures.
64
65
```python { .api }
66
class FailedToLoadPlugin(Flake8Exception):
67
"""
68
Exception raised when a plugin fails to load.
69
70
This exception occurs when flake8 cannot load a configured plugin,
71
typically due to import errors, missing dependencies, or plugin
72
code issues.
73
"""
74
75
def __init__(self, plugin_name: str, exception: Exception) -> None:
76
"""
77
Initialize plugin loading failure exception.
78
79
Parameters:
80
plugin_name: Name of the plugin that failed to load
81
exception: The underlying exception that caused the failure
82
"""
83
84
@property
85
def plugin_name(self) -> str:
86
"""Name of the plugin that failed to load."""
87
88
@property
89
def original_exception(self) -> Exception:
90
"""The underlying exception that caused the plugin loading failure."""
91
92
class PluginRequestedUnknownParameters(Flake8Exception):
93
"""
94
Exception raised when a plugin requests unknown parameters.
95
96
This exception occurs when a plugin tries to access configuration
97
parameters or options that are not recognized by flake8.
98
"""
99
100
def __init__(self, plugin_name: str, exception: Exception) -> None:
101
"""
102
Initialize unknown parameters exception.
103
104
Parameters:
105
plugin_name: Name of the plugin requesting unknown parameters
106
exception: The underlying exception from the parameter request
107
"""
108
109
@property
110
def plugin_name(self) -> str:
111
"""Name of the plugin that requested unknown parameters."""
112
113
@property
114
def original_exception(self) -> Exception:
115
"""The underlying exception from the parameter request."""
116
117
class PluginExecutionFailed(Flake8Exception):
118
"""
119
Exception raised when a plugin fails during execution.
120
121
This exception occurs when a plugin encounters an error while
122
processing a file or performing its checking operations.
123
"""
124
125
def __init__(self, filename: str, plugin_name: str, exception: Exception) -> None:
126
"""
127
Initialize plugin execution failure exception.
128
129
Parameters:
130
filename: Path to the file being processed when the failure occurred
131
plugin_name: Name of the plugin that failed during execution
132
exception: The underlying exception that caused the execution failure
133
"""
134
135
@property
136
def filename(self) -> str:
137
"""Path to the file being processed when the plugin failed."""
138
139
@property
140
def plugin_name(self) -> str:
141
"""Name of the plugin that failed during execution."""
142
143
@property
144
def original_exception(self) -> Exception:
145
"""The underlying exception that caused the execution failure."""
146
```
147
148
## Exception Handling Examples
149
150
### Basic Exception Handling
151
152
```python
153
from flake8.api import legacy
154
from flake8 import exceptions
155
156
def safe_flake8_check(files):
157
"""Run flake8 with comprehensive exception handling."""
158
159
try:
160
style_guide = legacy.get_style_guide()
161
report = style_guide.check_files(files)
162
return report.total_errors
163
164
except exceptions.EarlyQuit:
165
print("⚠️ Flake8 execution was interrupted by user")
166
return -1
167
168
except exceptions.ExecutionError as e:
169
print(f"❌ Flake8 execution failed: {e}")
170
return -1
171
172
except exceptions.FailedToLoadPlugin as e:
173
print(f"❌ Plugin loading failed: {e.plugin_name}")
174
print(f" Reason: {e.original_exception}")
175
return -1
176
177
except exceptions.PluginExecutionFailed as e:
178
print(f"❌ Plugin execution failed: {e.plugin_name}")
179
print(f" File: {e.filename}")
180
print(f" Error: {e.original_exception}")
181
return -1
182
183
except exceptions.PluginRequestedUnknownParameters as e:
184
print(f"❌ Plugin configuration error: {e.plugin_name}")
185
print(f" Reason: {e.original_exception}")
186
return -1
187
188
except exceptions.Flake8Exception as e:
189
print(f"❌ General flake8 error: {e}")
190
return -1
191
192
except Exception as e:
193
print(f"💥 Unexpected error: {e}")
194
return -1
195
196
# Usage
197
result = safe_flake8_check(['myproject/'])
198
if result >= 0:
199
print(f"Found {result} violations")
200
else:
201
print("Check failed due to errors")
202
```
203
204
### Plugin Error Recovery
205
206
```python
207
from flake8.api import legacy
208
from flake8 import exceptions
209
import logging
210
211
class RobustFlake8Runner:
212
"""Flake8 runner with plugin error recovery."""
213
214
def __init__(self):
215
self.failed_plugins = set()
216
self.logger = logging.getLogger(__name__)
217
218
def run_with_fallback(self, files, plugins=None):
219
"""Run flake8 with automatic plugin fallback."""
220
221
plugins = plugins or []
222
working_plugins = [p for p in plugins if p not in self.failed_plugins]
223
224
try:
225
# Try with current working plugins
226
style_guide = legacy.get_style_guide(
227
enable_extensions=working_plugins
228
)
229
report = style_guide.check_files(files)
230
return report.total_errors
231
232
except exceptions.FailedToLoadPlugin as e:
233
self.logger.warning(f"Plugin {e.plugin_name} failed to load: {e.original_exception}")
234
self.failed_plugins.add(e.plugin_name)
235
236
# Retry without the failed plugin
237
if e.plugin_name in working_plugins:
238
working_plugins.remove(e.plugin_name)
239
return self.run_with_fallback(files, working_plugins)
240
241
# If no plugins left, try with defaults
242
return self._run_default(files)
243
244
except exceptions.PluginExecutionFailed as e:
245
self.logger.error(f"Plugin {e.plugin_name} failed on {e.filename}: {e.original_exception}")
246
self.failed_plugins.add(e.plugin_name)
247
248
# Retry without the failed plugin
249
if e.plugin_name in working_plugins:
250
working_plugins.remove(e.plugin_name)
251
return self.run_with_fallback(files, working_plugins)
252
253
return self._run_default(files)
254
255
except exceptions.PluginRequestedUnknownParameters as e:
256
self.logger.warning(f"Plugin {e.plugin_name} has configuration issues: {e.original_exception}")
257
self.failed_plugins.add(e.plugin_name)
258
259
# Retry without the problematic plugin
260
if e.plugin_name in working_plugins:
261
working_plugins.remove(e.plugin_name)
262
return self.run_with_fallback(files, working_plugins)
263
264
return self._run_default(files)
265
266
def _run_default(self, files):
267
"""Run flake8 with only default built-in checkers."""
268
269
try:
270
self.logger.info("Running with default checkers only")
271
style_guide = legacy.get_style_guide()
272
report = style_guide.check_files(files)
273
return report.total_errors
274
275
except exceptions.Flake8Exception as e:
276
self.logger.error(f"Even default flake8 failed: {e}")
277
raise
278
279
# Usage
280
runner = RobustFlake8Runner()
281
try:
282
violations = runner.run_with_fallback(
283
['myproject/'],
284
plugins=['flake8-bugbear', 'flake8-import-order', 'flake8-docstrings']
285
)
286
print(f"Found {violations} violations")
287
except Exception as e:
288
print(f"Complete failure: {e}")
289
```
290
291
### Detailed Error Reporting
292
293
```python
294
from flake8.api import legacy
295
from flake8 import exceptions
296
import traceback
297
import sys
298
299
def detailed_flake8_check(files, report_file=None):
300
"""Run flake8 with detailed error reporting."""
301
302
error_report = {
303
'success': False,
304
'violations': 0,
305
'errors': [],
306
'warnings': []
307
}
308
309
try:
310
style_guide = legacy.get_style_guide(show_source=True)
311
report = style_guide.check_files(files)
312
313
error_report['success'] = True
314
error_report['violations'] = report.total_errors
315
316
return error_report
317
318
except exceptions.EarlyQuit:
319
error_report['errors'].append({
320
'type': 'EarlyQuit',
321
'message': 'User interrupted execution',
322
'fatal': False
323
})
324
325
except exceptions.FailedToLoadPlugin as e:
326
error_report['errors'].append({
327
'type': 'PluginLoadError',
328
'plugin': e.plugin_name,
329
'message': str(e.original_exception),
330
'fatal': True,
331
'traceback': traceback.format_exc()
332
})
333
334
except exceptions.PluginExecutionFailed as e:
335
error_report['errors'].append({
336
'type': 'PluginExecutionError',
337
'plugin': e.plugin_name,
338
'file': e.filename,
339
'message': str(e.original_exception),
340
'fatal': False,
341
'traceback': traceback.format_exc()
342
})
343
344
except exceptions.PluginRequestedUnknownParameters as e:
345
error_report['warnings'].append({
346
'type': 'PluginConfigurationWarning',
347
'plugin': e.plugin_name,
348
'message': str(e.original_exception),
349
'suggestion': 'Check plugin documentation and configuration'
350
})
351
352
except exceptions.ExecutionError as e:
353
error_report['errors'].append({
354
'type': 'ExecutionError',
355
'message': str(e),
356
'fatal': True,
357
'traceback': traceback.format_exc()
358
})
359
360
except exceptions.Flake8Exception as e:
361
error_report['errors'].append({
362
'type': 'GeneralFlake8Error',
363
'message': str(e),
364
'fatal': True,
365
'traceback': traceback.format_exc()
366
})
367
368
except Exception as e:
369
error_report['errors'].append({
370
'type': 'UnexpectedError',
371
'message': str(e),
372
'fatal': True,
373
'traceback': traceback.format_exc()
374
})
375
376
# Save detailed report if requested
377
if report_file:
378
import json
379
with open(report_file, 'w') as f:
380
json.dump(error_report, f, indent=2)
381
382
return error_report
383
384
def print_error_report(error_report):
385
"""Print formatted error report."""
386
387
if error_report['success']:
388
print(f"✅ Success! Found {error_report['violations']} violations")
389
return
390
391
print("❌ Flake8 execution encountered errors:")
392
393
for error in error_report['errors']:
394
print(f"\n🔥 {error['type']}: {error['message']}")
395
if 'plugin' in error:
396
print(f" Plugin: {error['plugin']}")
397
if 'file' in error:
398
print(f" File: {error['file']}")
399
if error.get('fatal'):
400
print(" ⚠️ This is a fatal error")
401
402
for warning in error_report['warnings']:
403
print(f"\n⚠️ {warning['type']}: {warning['message']}")
404
if 'plugin' in warning:
405
print(f" Plugin: {warning['plugin']}")
406
if 'suggestion' in warning:
407
print(f" 💡 {warning['suggestion']}")
408
409
# Usage
410
if __name__ == "__main__":
411
files_to_check = sys.argv[1:] or ['.']
412
413
report = detailed_flake8_check(files_to_check, 'flake8-error-report.json')
414
print_error_report(report)
415
416
# Exit with appropriate code
417
if report['success']:
418
sys.exit(0 if report['violations'] == 0 else 1)
419
else:
420
sys.exit(2) # Error during execution
421
```