0
# Reporters and Output
1
2
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.
3
4
## Capabilities
5
6
### Base Reporter System
7
8
Foundation classes for all reporters providing the interface and common functionality.
9
10
```python { .api }
11
class BaseReporter:
12
"""
13
Abstract base class for all reporters.
14
15
Attributes:
16
out: Output stream for writing results
17
messages: List of collected messages
18
path_strip_prefix: Prefix to strip from file paths
19
"""
20
21
def __init__(self, output=None):
22
"""
23
Initialize the reporter.
24
25
Args:
26
output: Output stream (default: sys.stdout)
27
"""
28
29
def handle_message(self, msg):
30
"""
31
Handle a single message.
32
33
Args:
34
msg: Message instance to process
35
"""
36
37
def display_messages(self, layout):
38
"""
39
Display messages after collection.
40
41
Args:
42
layout: Layout object for organizing output (can be None)
43
"""
44
45
def writeln(self, string=''):
46
"""
47
Write a line to output.
48
49
Args:
50
string: String to write (default: empty line)
51
"""
52
53
def display_reports(self, layout):
54
"""
55
Display analysis reports.
56
57
Args:
58
layout: Report layout object
59
"""
60
61
def on_set_current_module(self, module, filepath):
62
"""
63
Called when switching to a new module.
64
65
Args:
66
module: Module name
67
filepath: Path to the module file
68
"""
69
```
70
71
### Built-in Reporters
72
73
Standard reporters for common output formats and use cases.
74
75
```python { .api }
76
class TextReporter(BaseReporter):
77
"""
78
Default text output reporter.
79
80
Outputs messages in human-readable format with file paths,
81
line numbers, message types, and descriptions.
82
"""
83
84
name = 'text'
85
extension = 'txt'
86
87
class ColorizedTextReporter(TextReporter):
88
"""
89
Text reporter with color output for terminals.
90
91
Adds ANSI color codes to highlight different message types
92
and improve readability in terminal environments.
93
"""
94
95
name = 'colorized'
96
97
class JSONReporter(BaseReporter):
98
"""
99
JSON format reporter for programmatic consumption.
100
101
Outputs messages as JSON array with structured data
102
suitable for parsing by other tools and scripts.
103
"""
104
105
name = 'json'
106
extension = 'json'
107
108
class JSON2Reporter(BaseReporter):
109
"""
110
Enhanced JSON reporter with additional metadata.
111
112
Includes extended message information, statistics,
113
and analysis metadata in JSON format.
114
"""
115
116
name = 'json2'
117
extension = 'json'
118
```
119
120
### Specialized Reporters
121
122
Reporters for specific use cases and integrations.
123
124
```python { .api }
125
class CollectingReporter(BaseReporter):
126
"""
127
Reporter that collects messages in memory.
128
129
Useful for programmatic access to analysis results
130
without writing to files or streams.
131
"""
132
133
name = 'collector'
134
135
def finalize(self):
136
"""
137
Finalize collection and return messages.
138
139
Returns:
140
list: Collected messages
141
"""
142
143
class MultiReporter(BaseReporter):
144
"""
145
Reporter that delegates to multiple sub-reporters.
146
147
Allows simultaneous output to multiple formats
148
(e.g., console display and file logging).
149
"""
150
151
def __init__(self, reporters):
152
"""
153
Initialize with multiple reporters.
154
155
Args:
156
reporters: List of reporter instances
157
"""
158
159
def add_reporter(self, reporter):
160
"""
161
Add a new reporter to the collection.
162
163
Args:
164
reporter: Reporter instance to add
165
"""
166
```
167
168
### Report Generation System
169
170
Mixin class providing report generation capabilities.
171
172
```python { .api }
173
class ReportsHandlerMixIn:
174
"""
175
Mixin providing report generation utilities.
176
177
Handles statistics collection, report formatting,
178
and output generation for analysis results.
179
"""
180
181
def add_stats(self, **kwargs):
182
"""
183
Add statistics to the report.
184
185
Args:
186
**kwargs: Statistics key-value pairs
187
"""
188
189
def make_reports(self, stats, old_stats):
190
"""
191
Generate analysis reports.
192
193
Args:
194
stats: Current analysis statistics
195
old_stats: Previous analysis statistics for comparison
196
"""
197
198
def generate_reports(self):
199
"""Generate and display all reports."""
200
```
201
202
### Custom Reporter Development
203
204
Framework for creating custom reporters for specific output formats or integrations.
205
206
```python { .api }
207
# Custom reporter pattern
208
class CustomReporter(BaseReporter):
209
"""Template for custom reporter implementation."""
210
211
name = 'custom' # Unique name for the reporter
212
extension = 'ext' # File extension for output
213
214
def __init__(self, output=None):
215
"""Initialize custom reporter."""
216
super().__init__(output)
217
self.custom_data = {}
218
219
def handle_message(self, msg):
220
"""Process each message according to custom format."""
221
# Custom message processing logic
222
formatted_msg = self.format_message(msg)
223
self.writeln(formatted_msg)
224
225
def format_message(self, msg):
226
"""Format message according to custom requirements."""
227
return f"Custom: {msg.path}:{msg.line} - {msg.msg}"
228
229
def display_reports(self, layout):
230
"""Generate custom report format."""
231
# Custom report generation logic
232
pass
233
```
234
235
## Usage Examples
236
237
### Using Built-in Reporters
238
239
```python
240
from pylint.lint import PyLinter
241
from pylint.reporters import TextReporter, JSONReporter
242
import sys
243
244
# Text reporter to stdout
245
linter = PyLinter()
246
text_reporter = TextReporter(sys.stdout)
247
linter.set_reporter(text_reporter)
248
linter.check(['mymodule.py'])
249
250
# JSON reporter to file
251
with open('pylint_results.json', 'w') as f:
252
json_reporter = JSONReporter(f)
253
linter.set_reporter(json_reporter)
254
linter.check(['mymodule.py'])
255
256
# Collecting reporter for programmatic access
257
from pylint.reporters import CollectingReporter
258
collector = CollectingReporter()
259
linter.set_reporter(collector)
260
linter.check(['mymodule.py'])
261
messages = collector.finalize()
262
for msg in messages:
263
print(f"{msg.msg_id}: {msg.msg}")
264
```
265
266
### Multiple Output Formats
267
268
```python
269
from pylint.reporters import MultiReporter, TextReporter, JSONReporter
270
import sys
271
272
# Output to both console and JSON file
273
text_reporter = TextReporter(sys.stdout)
274
with open('results.json', 'w') as f:
275
json_reporter = JSONReporter(f)
276
multi_reporter = MultiReporter([text_reporter, json_reporter])
277
278
linter = PyLinter()
279
linter.set_reporter(multi_reporter)
280
linter.check(['mymodule.py'])
281
```
282
283
### Custom Reporter Implementation
284
285
```python
286
from pylint.reporters import BaseReporter
287
import xml.etree.ElementTree as ET
288
289
class XMLReporter(BaseReporter):
290
"""Custom XML reporter for test result integration."""
291
292
name = 'xml'
293
extension = 'xml'
294
295
def __init__(self, output=None):
296
super().__init__(output)
297
self.root = ET.Element('pylint-results')
298
self.current_file = None
299
300
def on_set_current_module(self, module, filepath):
301
"""Create XML element for new module."""
302
self.current_file = ET.SubElement(self.root, 'file')
303
self.current_file.set('name', filepath)
304
305
def handle_message(self, msg):
306
"""Add message as XML element."""
307
if self.current_file is not None:
308
issue = ET.SubElement(self.current_file, 'issue')
309
issue.set('line', str(msg.line))
310
issue.set('column', str(msg.column))
311
issue.set('severity', msg.category)
312
issue.set('message', msg.msg)
313
issue.set('message-id', msg.msg_id)
314
315
def display_reports(self, layout):
316
"""Output final XML."""
317
tree = ET.ElementTree(self.root)
318
tree.write(self.out, encoding='unicode', xml_declaration=True)
319
320
# Register and use custom reporter
321
def register_reporter(reporter_class):
322
"""Register custom reporter with pylint."""
323
# Registration logic would go here
324
pass
325
326
register_reporter(XMLReporter)
327
```
328
329
### IDE Integration Reporter
330
331
```python
332
class IDEReporter(BaseReporter):
333
"""Reporter optimized for IDE integration."""
334
335
name = 'ide'
336
337
def __init__(self, output=None):
338
super().__init__(output)
339
self.messages_by_file = {}
340
341
def handle_message(self, msg):
342
"""Group messages by file for IDE consumption."""
343
if msg.path not in self.messages_by_file:
344
self.messages_by_file[msg.path] = []
345
346
self.messages_by_file[msg.path].append({
347
'line': msg.line,
348
'column': msg.column,
349
'severity': self.map_severity(msg.category),
350
'message': msg.msg,
351
'rule': msg.msg_id
352
})
353
354
def map_severity(self, category):
355
"""Map pylint categories to IDE severity levels."""
356
mapping = {
357
'E': 'error',
358
'F': 'error',
359
'W': 'warning',
360
'C': 'info',
361
'R': 'info',
362
'I': 'info'
363
}
364
return mapping.get(category, 'info')
365
366
def display_reports(self, layout):
367
"""Output IDE-friendly format."""
368
import json
369
result = {
370
'files': self.messages_by_file,
371
'summary': {
372
'total_files': len(self.messages_by_file),
373
'total_issues': sum(len(msgs) for msgs in self.messages_by_file.values())
374
}
375
}
376
json.dump(result, self.out, indent=2)
377
```
378
379
## Output Format Examples
380
381
### Text Reporter Output
382
383
```
384
************* Module mymodule
385
mymodule.py:10:0: C0111: Missing module docstring (missing-docstring)
386
mymodule.py:15:4: W0613: Unused argument 'param' (unused-argument)
387
mymodule.py:20:0: R0903: Too few public methods (1/2) (too-few-public-methods)
388
```
389
390
### JSON Reporter Output
391
392
```json
393
[
394
{
395
"type": "convention",
396
"module": "mymodule",
397
"obj": "",
398
"line": 10,
399
"column": 0,
400
"path": "mymodule.py",
401
"symbol": "missing-docstring",
402
"message": "Missing module docstring",
403
"message-id": "C0111"
404
},
405
{
406
"type": "warning",
407
"module": "mymodule",
408
"obj": "function_name",
409
"line": 15,
410
"column": 4,
411
"path": "mymodule.py",
412
"symbol": "unused-argument",
413
"message": "Unused argument 'param'",
414
"message-id": "W0613"
415
}
416
]
417
```
418
419
### Colorized Output
420
421
Terminal output with ANSI color codes for different message types:
422
- **Red**: Errors and fatal messages
423
- **Yellow**: Warnings
424
- **Blue**: Conventions and refactoring suggestions
425
- **Green**: Informational messages
426
427
## Reporter Configuration
428
429
### Command Line Reporter Selection
430
431
```bash
432
# Use specific reporter
433
pylint --output-format=json mymodule.py
434
pylint --output-format=colorized mymodule.py
435
436
# Output to file
437
pylint --output-format=json --output=results.json mymodule.py
438
439
# Use multiple reporters (with custom script)
440
pylint --load-plugins=custom_reporters --output-format=multi mymodule.py
441
```
442
443
### Programmatic Reporter Configuration
444
445
```python
446
from pylint.lint import PyLinter
447
from pylint.reporters import TextReporter
448
449
linter = PyLinter()
450
451
# Configure reporter options
452
reporter = TextReporter()
453
reporter.path_strip_prefix = '/project/root/'
454
455
linter.set_reporter(reporter)
456
457
# Configure report generation
458
linter.config.reports = True
459
linter.config.score = True
460
linter.config.msg_template = '{path}:{line}: {msg_id}: {msg}'
461
```