Comprehensive static code analysis tool for Python that performs deep code inspection without executing the program
—
Flexible reporting system supporting multiple output formats and custom reporter development. Pylint's reporter architecture allows integration with different tools, IDEs, and continuous integration systems through standardized and custom output formats.
Foundation classes for all reporters providing the interface and common functionality.
class BaseReporter:
"""
Abstract base class for all reporters.
Attributes:
out: Output stream for writing results
messages: List of collected messages
path_strip_prefix: Prefix to strip from file paths
"""
def __init__(self, output=None):
"""
Initialize the reporter.
Args:
output: Output stream (default: sys.stdout)
"""
def handle_message(self, msg):
"""
Handle a single message.
Args:
msg: Message instance to process
"""
def display_messages(self, layout):
"""
Display messages after collection.
Args:
layout: Layout object for organizing output (can be None)
"""
def writeln(self, string=''):
"""
Write a line to output.
Args:
string: String to write (default: empty line)
"""
def display_reports(self, layout):
"""
Display analysis reports.
Args:
layout: Report layout object
"""
def on_set_current_module(self, module, filepath):
"""
Called when switching to a new module.
Args:
module: Module name
filepath: Path to the module file
"""Standard reporters for common output formats and use cases.
class TextReporter(BaseReporter):
"""
Default text output reporter.
Outputs messages in human-readable format with file paths,
line numbers, message types, and descriptions.
"""
name = 'text'
extension = 'txt'
class ColorizedTextReporter(TextReporter):
"""
Text reporter with color output for terminals.
Adds ANSI color codes to highlight different message types
and improve readability in terminal environments.
"""
name = 'colorized'
class JSONReporter(BaseReporter):
"""
JSON format reporter for programmatic consumption.
Outputs messages as JSON array with structured data
suitable for parsing by other tools and scripts.
"""
name = 'json'
extension = 'json'
class JSON2Reporter(BaseReporter):
"""
Enhanced JSON reporter with additional metadata.
Includes extended message information, statistics,
and analysis metadata in JSON format.
"""
name = 'json2'
extension = 'json'Reporters for specific use cases and integrations.
class CollectingReporter(BaseReporter):
"""
Reporter that collects messages in memory.
Useful for programmatic access to analysis results
without writing to files or streams.
"""
name = 'collector'
def finalize(self):
"""
Finalize collection and return messages.
Returns:
list: Collected messages
"""
class MultiReporter(BaseReporter):
"""
Reporter that delegates to multiple sub-reporters.
Allows simultaneous output to multiple formats
(e.g., console display and file logging).
"""
def __init__(self, reporters):
"""
Initialize with multiple reporters.
Args:
reporters: List of reporter instances
"""
def add_reporter(self, reporter):
"""
Add a new reporter to the collection.
Args:
reporter: Reporter instance to add
"""Mixin class providing report generation capabilities.
class ReportsHandlerMixIn:
"""
Mixin providing report generation utilities.
Handles statistics collection, report formatting,
and output generation for analysis results.
"""
def add_stats(self, **kwargs):
"""
Add statistics to the report.
Args:
**kwargs: Statistics key-value pairs
"""
def make_reports(self, stats, old_stats):
"""
Generate analysis reports.
Args:
stats: Current analysis statistics
old_stats: Previous analysis statistics for comparison
"""
def generate_reports(self):
"""Generate and display all reports."""Framework for creating custom reporters for specific output formats or integrations.
# Custom reporter pattern
class CustomReporter(BaseReporter):
"""Template for custom reporter implementation."""
name = 'custom' # Unique name for the reporter
extension = 'ext' # File extension for output
def __init__(self, output=None):
"""Initialize custom reporter."""
super().__init__(output)
self.custom_data = {}
def handle_message(self, msg):
"""Process each message according to custom format."""
# Custom message processing logic
formatted_msg = self.format_message(msg)
self.writeln(formatted_msg)
def format_message(self, msg):
"""Format message according to custom requirements."""
return f"Custom: {msg.path}:{msg.line} - {msg.msg}"
def display_reports(self, layout):
"""Generate custom report format."""
# Custom report generation logic
passfrom pylint.lint import PyLinter
from pylint.reporters import TextReporter, JSONReporter
import sys
# Text reporter to stdout
linter = PyLinter()
text_reporter = TextReporter(sys.stdout)
linter.set_reporter(text_reporter)
linter.check(['mymodule.py'])
# JSON reporter to file
with open('pylint_results.json', 'w') as f:
json_reporter = JSONReporter(f)
linter.set_reporter(json_reporter)
linter.check(['mymodule.py'])
# Collecting reporter for programmatic access
from pylint.reporters import CollectingReporter
collector = CollectingReporter()
linter.set_reporter(collector)
linter.check(['mymodule.py'])
messages = collector.finalize()
for msg in messages:
print(f"{msg.msg_id}: {msg.msg}")from pylint.reporters import MultiReporter, TextReporter, JSONReporter
import sys
# Output to both console and JSON file
text_reporter = TextReporter(sys.stdout)
with open('results.json', 'w') as f:
json_reporter = JSONReporter(f)
multi_reporter = MultiReporter([text_reporter, json_reporter])
linter = PyLinter()
linter.set_reporter(multi_reporter)
linter.check(['mymodule.py'])from pylint.reporters import BaseReporter
import xml.etree.ElementTree as ET
class XMLReporter(BaseReporter):
"""Custom XML reporter for test result integration."""
name = 'xml'
extension = 'xml'
def __init__(self, output=None):
super().__init__(output)
self.root = ET.Element('pylint-results')
self.current_file = None
def on_set_current_module(self, module, filepath):
"""Create XML element for new module."""
self.current_file = ET.SubElement(self.root, 'file')
self.current_file.set('name', filepath)
def handle_message(self, msg):
"""Add message as XML element."""
if self.current_file is not None:
issue = ET.SubElement(self.current_file, 'issue')
issue.set('line', str(msg.line))
issue.set('column', str(msg.column))
issue.set('severity', msg.category)
issue.set('message', msg.msg)
issue.set('message-id', msg.msg_id)
def display_reports(self, layout):
"""Output final XML."""
tree = ET.ElementTree(self.root)
tree.write(self.out, encoding='unicode', xml_declaration=True)
# Register and use custom reporter
def register_reporter(reporter_class):
"""Register custom reporter with pylint."""
# Registration logic would go here
pass
register_reporter(XMLReporter)class IDEReporter(BaseReporter):
"""Reporter optimized for IDE integration."""
name = 'ide'
def __init__(self, output=None):
super().__init__(output)
self.messages_by_file = {}
def handle_message(self, msg):
"""Group messages by file for IDE consumption."""
if msg.path not in self.messages_by_file:
self.messages_by_file[msg.path] = []
self.messages_by_file[msg.path].append({
'line': msg.line,
'column': msg.column,
'severity': self.map_severity(msg.category),
'message': msg.msg,
'rule': msg.msg_id
})
def map_severity(self, category):
"""Map pylint categories to IDE severity levels."""
mapping = {
'E': 'error',
'F': 'error',
'W': 'warning',
'C': 'info',
'R': 'info',
'I': 'info'
}
return mapping.get(category, 'info')
def display_reports(self, layout):
"""Output IDE-friendly format."""
import json
result = {
'files': self.messages_by_file,
'summary': {
'total_files': len(self.messages_by_file),
'total_issues': sum(len(msgs) for msgs in self.messages_by_file.values())
}
}
json.dump(result, self.out, indent=2)************* Module mymodule
mymodule.py:10:0: C0111: Missing module docstring (missing-docstring)
mymodule.py:15:4: W0613: Unused argument 'param' (unused-argument)
mymodule.py:20:0: R0903: Too few public methods (1/2) (too-few-public-methods)[
{
"type": "convention",
"module": "mymodule",
"obj": "",
"line": 10,
"column": 0,
"path": "mymodule.py",
"symbol": "missing-docstring",
"message": "Missing module docstring",
"message-id": "C0111"
},
{
"type": "warning",
"module": "mymodule",
"obj": "function_name",
"line": 15,
"column": 4,
"path": "mymodule.py",
"symbol": "unused-argument",
"message": "Unused argument 'param'",
"message-id": "W0613"
}
]Terminal output with ANSI color codes for different message types:
# Use specific reporter
pylint --output-format=json mymodule.py
pylint --output-format=colorized mymodule.py
# Output to file
pylint --output-format=json --output=results.json mymodule.py
# Use multiple reporters (with custom script)
pylint --load-plugins=custom_reporters --output-format=multi mymodule.pyfrom pylint.lint import PyLinter
from pylint.reporters import TextReporter
linter = PyLinter()
# Configure reporter options
reporter = TextReporter()
reporter.path_strip_prefix = '/project/root/'
linter.set_reporter(reporter)
# Configure report generation
linter.config.reports = True
linter.config.score = True
linter.config.msg_template = '{path}:{line}: {msg_id}: {msg}'Install with Tessl CLI
npx tessl i tessl/pypi-pylint