A comprehensive Python code quality checking tool that wraps PyFlakes, pycodestyle, and McCabe to provide unified static analysis with extensible plugin support.
—
Comprehensive exception hierarchy for handling errors during flake8 execution, including plugin loading failures, execution errors, and early termination scenarios.
Base exception class for all flake8-specific errors.
class Flake8Exception(Exception):
"""
Base exception class for all flake8-specific errors.
All flake8-related exceptions inherit from this class, allowing
for easy exception handling of flake8-specific issues.
Example:
try:
# flake8 operations
except Flake8Exception as e:
# Handle any flake8-specific error
"""Exceptions related to execution flow and early termination.
class EarlyQuit(Flake8Exception):
"""
Exception raised when flake8 encounters a KeyboardInterrupt.
This exception is raised when the user interrupts flake8 execution
(typically with Ctrl+C) to provide clean termination handling.
Example:
try:
style_guide.check_files(large_file_list)
except EarlyQuit:
print("Flake8 execution was interrupted by user")
"""
class ExecutionError(Flake8Exception):
"""
Exception raised during general execution of flake8.
This exception covers various execution errors that can occur
during flake8's operation that are not covered by more specific
exception types.
Example:
try:
app.run(invalid_args)
except ExecutionError as e:
print(f"Execution failed: {e}")
"""Exceptions specifically related to plugin loading and execution failures.
class FailedToLoadPlugin(Flake8Exception):
"""
Exception raised when a plugin fails to load.
This exception occurs when flake8 cannot load a configured plugin,
typically due to import errors, missing dependencies, or plugin
code issues.
"""
def __init__(self, plugin_name: str, exception: Exception) -> None:
"""
Initialize plugin loading failure exception.
Parameters:
plugin_name: Name of the plugin that failed to load
exception: The underlying exception that caused the failure
"""
@property
def plugin_name(self) -> str:
"""Name of the plugin that failed to load."""
@property
def original_exception(self) -> Exception:
"""The underlying exception that caused the plugin loading failure."""
class PluginRequestedUnknownParameters(Flake8Exception):
"""
Exception raised when a plugin requests unknown parameters.
This exception occurs when a plugin tries to access configuration
parameters or options that are not recognized by flake8.
"""
def __init__(self, plugin_name: str, exception: Exception) -> None:
"""
Initialize unknown parameters exception.
Parameters:
plugin_name: Name of the plugin requesting unknown parameters
exception: The underlying exception from the parameter request
"""
@property
def plugin_name(self) -> str:
"""Name of the plugin that requested unknown parameters."""
@property
def original_exception(self) -> Exception:
"""The underlying exception from the parameter request."""
class PluginExecutionFailed(Flake8Exception):
"""
Exception raised when a plugin fails during execution.
This exception occurs when a plugin encounters an error while
processing a file or performing its checking operations.
"""
def __init__(self, filename: str, plugin_name: str, exception: Exception) -> None:
"""
Initialize plugin execution failure exception.
Parameters:
filename: Path to the file being processed when the failure occurred
plugin_name: Name of the plugin that failed during execution
exception: The underlying exception that caused the execution failure
"""
@property
def filename(self) -> str:
"""Path to the file being processed when the plugin failed."""
@property
def plugin_name(self) -> str:
"""Name of the plugin that failed during execution."""
@property
def original_exception(self) -> Exception:
"""The underlying exception that caused the execution failure."""from flake8.api import legacy
from flake8 import exceptions
def safe_flake8_check(files):
"""Run flake8 with comprehensive exception handling."""
try:
style_guide = legacy.get_style_guide()
report = style_guide.check_files(files)
return report.total_errors
except exceptions.EarlyQuit:
print("⚠️ Flake8 execution was interrupted by user")
return -1
except exceptions.ExecutionError as e:
print(f"❌ Flake8 execution failed: {e}")
return -1
except exceptions.FailedToLoadPlugin as e:
print(f"❌ Plugin loading failed: {e.plugin_name}")
print(f" Reason: {e.original_exception}")
return -1
except exceptions.PluginExecutionFailed as e:
print(f"❌ Plugin execution failed: {e.plugin_name}")
print(f" File: {e.filename}")
print(f" Error: {e.original_exception}")
return -1
except exceptions.PluginRequestedUnknownParameters as e:
print(f"❌ Plugin configuration error: {e.plugin_name}")
print(f" Reason: {e.original_exception}")
return -1
except exceptions.Flake8Exception as e:
print(f"❌ General flake8 error: {e}")
return -1
except Exception as e:
print(f"💥 Unexpected error: {e}")
return -1
# Usage
result = safe_flake8_check(['myproject/'])
if result >= 0:
print(f"Found {result} violations")
else:
print("Check failed due to errors")from flake8.api import legacy
from flake8 import exceptions
import logging
class RobustFlake8Runner:
"""Flake8 runner with plugin error recovery."""
def __init__(self):
self.failed_plugins = set()
self.logger = logging.getLogger(__name__)
def run_with_fallback(self, files, plugins=None):
"""Run flake8 with automatic plugin fallback."""
plugins = plugins or []
working_plugins = [p for p in plugins if p not in self.failed_plugins]
try:
# Try with current working plugins
style_guide = legacy.get_style_guide(
enable_extensions=working_plugins
)
report = style_guide.check_files(files)
return report.total_errors
except exceptions.FailedToLoadPlugin as e:
self.logger.warning(f"Plugin {e.plugin_name} failed to load: {e.original_exception}")
self.failed_plugins.add(e.plugin_name)
# Retry without the failed plugin
if e.plugin_name in working_plugins:
working_plugins.remove(e.plugin_name)
return self.run_with_fallback(files, working_plugins)
# If no plugins left, try with defaults
return self._run_default(files)
except exceptions.PluginExecutionFailed as e:
self.logger.error(f"Plugin {e.plugin_name} failed on {e.filename}: {e.original_exception}")
self.failed_plugins.add(e.plugin_name)
# Retry without the failed plugin
if e.plugin_name in working_plugins:
working_plugins.remove(e.plugin_name)
return self.run_with_fallback(files, working_plugins)
return self._run_default(files)
except exceptions.PluginRequestedUnknownParameters as e:
self.logger.warning(f"Plugin {e.plugin_name} has configuration issues: {e.original_exception}")
self.failed_plugins.add(e.plugin_name)
# Retry without the problematic plugin
if e.plugin_name in working_plugins:
working_plugins.remove(e.plugin_name)
return self.run_with_fallback(files, working_plugins)
return self._run_default(files)
def _run_default(self, files):
"""Run flake8 with only default built-in checkers."""
try:
self.logger.info("Running with default checkers only")
style_guide = legacy.get_style_guide()
report = style_guide.check_files(files)
return report.total_errors
except exceptions.Flake8Exception as e:
self.logger.error(f"Even default flake8 failed: {e}")
raise
# Usage
runner = RobustFlake8Runner()
try:
violations = runner.run_with_fallback(
['myproject/'],
plugins=['flake8-bugbear', 'flake8-import-order', 'flake8-docstrings']
)
print(f"Found {violations} violations")
except Exception as e:
print(f"Complete failure: {e}")from flake8.api import legacy
from flake8 import exceptions
import traceback
import sys
def detailed_flake8_check(files, report_file=None):
"""Run flake8 with detailed error reporting."""
error_report = {
'success': False,
'violations': 0,
'errors': [],
'warnings': []
}
try:
style_guide = legacy.get_style_guide(show_source=True)
report = style_guide.check_files(files)
error_report['success'] = True
error_report['violations'] = report.total_errors
return error_report
except exceptions.EarlyQuit:
error_report['errors'].append({
'type': 'EarlyQuit',
'message': 'User interrupted execution',
'fatal': False
})
except exceptions.FailedToLoadPlugin as e:
error_report['errors'].append({
'type': 'PluginLoadError',
'plugin': e.plugin_name,
'message': str(e.original_exception),
'fatal': True,
'traceback': traceback.format_exc()
})
except exceptions.PluginExecutionFailed as e:
error_report['errors'].append({
'type': 'PluginExecutionError',
'plugin': e.plugin_name,
'file': e.filename,
'message': str(e.original_exception),
'fatal': False,
'traceback': traceback.format_exc()
})
except exceptions.PluginRequestedUnknownParameters as e:
error_report['warnings'].append({
'type': 'PluginConfigurationWarning',
'plugin': e.plugin_name,
'message': str(e.original_exception),
'suggestion': 'Check plugin documentation and configuration'
})
except exceptions.ExecutionError as e:
error_report['errors'].append({
'type': 'ExecutionError',
'message': str(e),
'fatal': True,
'traceback': traceback.format_exc()
})
except exceptions.Flake8Exception as e:
error_report['errors'].append({
'type': 'GeneralFlake8Error',
'message': str(e),
'fatal': True,
'traceback': traceback.format_exc()
})
except Exception as e:
error_report['errors'].append({
'type': 'UnexpectedError',
'message': str(e),
'fatal': True,
'traceback': traceback.format_exc()
})
# Save detailed report if requested
if report_file:
import json
with open(report_file, 'w') as f:
json.dump(error_report, f, indent=2)
return error_report
def print_error_report(error_report):
"""Print formatted error report."""
if error_report['success']:
print(f"✅ Success! Found {error_report['violations']} violations")
return
print("❌ Flake8 execution encountered errors:")
for error in error_report['errors']:
print(f"\n🔥 {error['type']}: {error['message']}")
if 'plugin' in error:
print(f" Plugin: {error['plugin']}")
if 'file' in error:
print(f" File: {error['file']}")
if error.get('fatal'):
print(" ⚠️ This is a fatal error")
for warning in error_report['warnings']:
print(f"\n⚠️ {warning['type']}: {warning['message']}")
if 'plugin' in warning:
print(f" Plugin: {warning['plugin']}")
if 'suggestion' in warning:
print(f" 💡 {warning['suggestion']}")
# Usage
if __name__ == "__main__":
files_to_check = sys.argv[1:] or ['.']
report = detailed_flake8_check(files_to_check, 'flake8-error-report.json')
print_error_report(report)
# Exit with appropriate code
if report['success']:
sys.exit(0 if report['violations'] == 0 else 1)
else:
sys.exit(2) # Error during executionInstall with Tessl CLI
npx tessl i tessl/pypi-flake8