Code audit tool for python
—
Error representation, deduplication, and formatting capabilities for managing linting results across multiple tools and output formats. Pylama provides comprehensive error handling with intelligent deduplication and flexible formatting options.
from typing import Any, Dict, Generator, List, Optional, Set, Tuple
from argparse import NamespaceCore error class that represents individual linting issues with comprehensive metadata.
class Error:
"""
Represents a single linting error with source information.
Attributes:
filename: str - File path where error occurred
lnum: int - Line number (1-based)
col: int - Column number (1-based)
message: str - Error message text
etype: str - Error type (E=error, W=warning, etc.)
source: str - Linter that generated the error
number: str - Error code (extracted from text)
"""
def __init__(
self,
source: str = "pylama",
col: int = 1,
lnum: int = 1,
type: Optional[str] = None,
text: str = "unknown error",
filename: str = "",
number: str = "",
**_
):
"""
Initialize error with position and message information.
Args:
source: Name of linter that found the error
col: Column number (1-based)
lnum: Line number (1-based)
type: Error type/severity (stored as etype attribute)
text: Error message text (stored as message attribute)
filename: File path where error occurred
number: Error code number
**_: Additional unused parameters
"""
def to_dict(self) -> Dict[str, Any]:
"""
Convert error to dictionary representation.
Returns:
Dict[str, Any]: Error data as dictionary with keys:
- filename, lnum, col, message, etype, source, number
"""
def format(self, template: str) -> str:
"""
Format error using template string.
Args:
template: Format string with placeholders like {filename}, {lnum}, etc.
Returns:
str: Formatted error message
Available template variables:
- {filename}: File path
- {lnum}: Line number
- {col}: Column number
- {message}: Error message text
- {etype}: Error type
- {source}: Linter name
- {number}: Error code
"""Remove duplicate errors that are reported by multiple linters for the same issue.
def remove_duplicates(errors: List[Error]) -> Generator[Error, None, None]:
"""
Remove duplicate errors from different linters.
Args:
errors: List of Error objects potentially containing duplicates
Yields:
Error: Unique errors with duplicates removed
Deduplication rules:
- Errors at same location with equivalent meanings are deduplicated
- Priority given to more specific linters (e.g., pycodestyle over pylint for style)
- Common duplicate patterns handled automatically (see DUPLICATES mapping)
"""Sort errors according to configurable criteria.
def default_sorter(err: Error) -> Any:
"""
Default error sorting function.
Args:
err: Error object to generate sort key for
Returns:
Any: Sort key (typically line number for default sorter)
Default sort order:
- Line number (ascending)
"""Format and output errors using various output formats.
def display_errors(errors: List[Error], options: Namespace):
"""
Format and display errors using specified format.
Args:
errors: List of Error objects to display
options: Configuration options containing format settings
Supported formats:
- 'json': JSON array of error dictionaries
- 'pylint': Pylint-compatible format
- 'pycodestyle': pycodestyle-compatible format
- 'parsable': Default parsable format (same as default)
- Custom format strings
Output is sent to the configured logger at WARNING level.
"""Pylama automatically deduplicates common errors reported by multiple linters:
DUPLICATES: Dict[Tuple[str, str], Set] = {
# Multiple statements on one line
("pycodestyle", "E701"): {("pylint", "C0321")},
# Unused variable
("pylint", "W0612"): {("pyflakes", "W0612")},
# Undefined variable
("pylint", "E0602"): {("pyflakes", "E0602")},
# Unused import
("pylint", "W0611"): {("pyflakes", "W0611")},
# Whitespace issues
("pylint", "C0326"): {
("pycodestyle", "E202"), # whitespace before ')'
("pycodestyle", "E211"), # whitespace before '('
("pycodestyle", "E222"), # multiple spaces after operator
("pycodestyle", "E225"), # missing whitespace around operator
("pycodestyle", "E251"), # unexpected spaces
},
# Long lines
("pylint", "C0301"): {("pycodestyle", "E501")},
# Other common duplicates...
}from pylama.main import check_paths
from pylama.config import parse_options
from pylama.errors import remove_duplicates
# Get errors from checking
options = parse_options(['--linters=pycodestyle,pylint', 'myfile.py'])
errors = check_paths(['myfile.py'], options)
# Remove duplicates
unique_errors = list(remove_duplicates(errors))
print(f"Found {len(errors)} total, {len(unique_errors)} unique errors")from pylama.errors import Error
# Create error object
error = Error(
source="pycodestyle",
col=10,
lnum=42,
type="E",
text="E501 line too long (82 > 79 characters)",
filename="example.py"
)
# Format with different templates
default_format = "{filename}:{lnum}:{col} [{etype}] {number} {message} [{source}]"
pylint_format = "{filename}:{lnum}: [{etype}] {number} {message} [{source}]"
print(error.format(default_format))
print(error.format(pylint_format))
# Convert to dictionary
error_dict = error.to_dict()
print(error_dict)
# Access error message
print(f"Error message: {error.message}")import json
from pylama.main import check_paths
from pylama.config import parse_options
# Configure for JSON output
options = parse_options(['--format=json', '--linters=pyflakes', 'myfile.py'])
errors = check_paths(['myfile.py'], options)
# Process as JSON data
error_data = [error.to_dict() for error in errors]
json_output = json.dumps(error_data, indent=2)
print(json_output)
# Filter by error type
warnings = [e for e in errors if e.etype == 'W']
errors_only = [e for e in errors if e.etype == 'E']
# Access error messages
for error in errors:
print(f"File: {error.filename}, Message: {error.message}")from pylama.errors import Error
errors = [
Error(source="pycodestyle", col=5, lnum=10, type="E", text="E501 line too long", filename="file1.py"),
Error(source="pylint", col=1, lnum=5, type="W", text="W0612 unused variable", filename="file1.py"),
Error(source="pyflakes", col=1, lnum=1, type="F", text="F401 unused import", filename="file2.py"),
]
# Sort by severity (errors first, then warnings)
def severity_sorter(err):
severity_order = {'E': 0, 'F': 1, 'W': 2}
return (severity_order.get(err.etype, 999), err.filename, err.lnum)
sorted_errors = sorted(errors, key=severity_sorter)
# Sort by error source (linter)
def source_sorter(err):
return (err.source, err.filename, err.lnum)
sorted_by_source = sorted(errors, key=source_sorter)from collections import defaultdict
from pylama.main import check_paths
from pylama.config import parse_options
# Analyze error patterns
options = parse_options(['src/'])
errors = check_paths(['src/'], options)
# Group by error type
by_type = defaultdict(list)
for error in errors:
by_type[error.etype].append(error)
# Group by source linter
by_source = defaultdict(list)
for error in errors:
by_source[error.source].append(error)
# Group by file
by_file = defaultdict(list)
for error in errors:
by_file[error.filename].append(error)
# Print summary
print("Error Summary:")
print(f"Total errors: {len(errors)}")
print(f"Files with issues: {len(by_file)}")
print(f"Error types: {list(by_type.keys())}")
print(f"Linters used: {list(by_source.keys())}")
# Print messages for each error
for error in errors:
print(f"{error.filename}:{error.lnum} - {error.message}")DEFAULT_FORMAT: str = "{filename}:{lnum}:{col} [{etype}] {number} {message} [{source}]"
MESSAGE_FORMATS: Dict[str, str] = {
"pylint": "{filename}:{lnum}: [{etype}] {number} {message} [{source}]",
"pycodestyle": "{filename}:{lnum}:{col} {number} {message} [{source}]",
"parsable": DEFAULT_FORMAT,
}Install with Tessl CLI
npx tessl i tessl/pypi-pylama