0
# Formatters
1
2
Output formatting system supporting multiple formats including JSON, text, XML, and IDE integrations. Formatters convert analysis results into various output formats for different use cases.
3
4
## Capabilities
5
6
### Formatter Base Class
7
8
```python { .api }
9
class Formatter(ABC):
10
def __init__(self, summary: dict[str, Any], messages: list[Message], profile: ProspectorProfile, relative_to: Optional[Path]) -> None
11
```
12
13
Base class for all output formatters.
14
15
**Parameters:**
16
- `summary`: dict[str, Any] - Analysis summary information
17
- `messages`: list[Message] - List of analysis messages
18
- `profile`: ProspectorProfile - Configuration profile used
19
- `relative_to`: Optional[Path] - Base path for relativizing file paths in output
20
21
```python { .api }
22
@abstractmethod
23
def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str
24
```
25
26
Renders the analysis results in the formatter's specific format.
27
28
**Parameters:**
29
- `summary`: bool - Whether to include summary information in output
30
- `messages`: bool - Whether to include individual messages in output
31
- `profile`: bool - Whether to include profile information in output
32
33
**Returns:**
34
- `str` - Formatted output string
35
36
### Available Formatters
37
38
```python { .api }
39
FORMATTERS: dict[str, type[Formatter]]
40
```
41
42
Registry of all available output formatters.
43
44
**Available Formatters:**
45
46
#### Text-Based Formatters
47
48
```python { .api }
49
class TextFormatter(Formatter):
50
pass
51
```
52
53
Human-readable text output format. Default formatter that provides clean, readable output for console display.
54
55
```python { .api }
56
class GroupedFormatter(Formatter):
57
pass
58
```
59
60
Groups messages by file and displays them in a hierarchical format. Good for understanding issues per file.
61
62
```python { .api }
63
class EmacsFormatter(Formatter):
64
pass
65
```
66
67
Output format compatible with Emacs compilation mode. Allows Emacs users to jump directly to issue locations.
68
69
#### Machine-Readable Formatters
70
71
```python { .api }
72
class JsonFormatter(Formatter):
73
pass
74
```
75
76
JSON output format for programmatic consumption and API integration. Includes complete message and summary information.
77
78
```python { .api }
79
class YamlFormatter(Formatter):
80
pass
81
```
82
83
YAML output format for human-readable structured data. Alternative to JSON with better readability.
84
85
```python { .api }
86
class XunitFormatter(Formatter):
87
pass
88
```
89
90
XML output in xUnit/JUnit format for CI/CD integration. Compatible with test reporting systems.
91
92
#### IDE and Tool Integration
93
94
```python { .api }
95
class VSCodeFormatter(Formatter):
96
pass
97
```
98
99
Output format optimized for Visual Studio Code integration. Compatible with VS Code problem matcher patterns.
100
101
```python { .api }
102
class GitlabFormatter(Formatter):
103
pass
104
```
105
106
GitLab CI/CD compatible output format for merge request integration and code quality reporting.
107
108
#### Pylint Compatibility
109
110
```python { .api }
111
class PylintFormatter(Formatter):
112
pass
113
```
114
115
Output format that mimics Pylint's default output. Useful for tools expecting Pylint-style output.
116
117
```python { .api }
118
class PylintParseableFormatter(Formatter):
119
pass
120
```
121
122
Pylint-compatible parseable output format. Machine-readable format following Pylint conventions.
123
124
## Usage Examples
125
126
### Using Formatters Directly
127
128
```python
129
from prospector.formatters import FORMATTERS
130
from prospector.config import ProspectorConfig
131
from prospector.run import Prospector
132
133
# Run analysis
134
config = ProspectorConfig()
135
prospector = Prospector(config)
136
prospector.execute()
137
138
# Get results
139
messages = prospector.get_messages()
140
summary = prospector.get_summary()
141
142
# Create formatter
143
formatter_class = FORMATTERS["json"]
144
formatter = formatter_class(summary, messages, config.profile, None)
145
146
# Render output
147
output = formatter.render(summary=True, messages=True, profile=False)
148
print(output)
149
```
150
151
### Multiple Output Formats
152
153
```python
154
from prospector.formatters import FORMATTERS
155
from prospector.config import ProspectorConfig
156
from prospector.run import Prospector
157
158
# Run analysis once
159
config = ProspectorConfig()
160
prospector = Prospector(config)
161
prospector.execute()
162
163
messages = prospector.get_messages()
164
summary = prospector.get_summary()
165
166
# Generate multiple output formats
167
formats_to_generate = ["json", "text", "yaml"]
168
169
for format_name in formats_to_generate:
170
formatter_class = FORMATTERS[format_name]
171
formatter = formatter_class(summary, messages, config.profile, None)
172
173
output = formatter.render()
174
175
# Save to file
176
with open(f"analysis_results.{format_name}", "w") as f:
177
f.write(output)
178
179
print(f"Generated {format_name} output")
180
```
181
182
### Formatter Configuration Examples
183
184
```python
185
from prospector.formatters import FORMATTERS
186
from prospector.config import ProspectorConfig
187
from prospector.run import Prospector
188
from pathlib import Path
189
190
config = ProspectorConfig()
191
prospector = Prospector(config)
192
prospector.execute()
193
194
messages = prospector.get_messages()
195
summary = prospector.get_summary()
196
197
# Text formatter with relative paths
198
base_path = Path("/home/user/project")
199
text_formatter = FORMATTERS["text"](summary, messages, config.profile, base_path)
200
print("=== Text Output (with relative paths) ===")
201
print(text_formatter.render())
202
203
# JSON formatter with full information
204
json_formatter = FORMATTERS["json"](summary, messages, config.profile, None)
205
print("\n=== JSON Output (full info) ===")
206
print(json_formatter.render(summary=True, messages=True, profile=True))
207
208
# Messages only (no summary)
209
text_messages_only = FORMATTERS["text"](summary, messages, config.profile, None)
210
print("\n=== Messages Only ===")
211
print(text_messages_only.render(summary=False, messages=True, profile=False))
212
```
213
214
### CI/CD Integration
215
216
```python
217
from prospector.formatters import FORMATTERS
218
from prospector.config import ProspectorConfig
219
from prospector.run import Prospector
220
import json
221
222
def generate_ci_reports():
223
"""Generate reports for CI/CD pipeline"""
224
config = ProspectorConfig()
225
prospector = Prospector(config)
226
prospector.execute()
227
228
messages = prospector.get_messages()
229
summary = prospector.get_summary()
230
231
# GitLab CI format
232
gitlab_formatter = FORMATTERS["gitlab"](summary, messages, config.profile, None)
233
with open("gitlab-code-quality.json", "w") as f:
234
f.write(gitlab_formatter.render())
235
236
# JUnit XML format for test reporting
237
xunit_formatter = FORMATTERS["xunit"](summary, messages, config.profile, None)
238
with open("prospector-results.xml", "w") as f:
239
f.write(xunit_formatter.render())
240
241
# JSON for further processing
242
json_formatter = FORMATTERS["json"](summary, messages, config.profile, None)
243
with open("prospector-results.json", "w") as f:
244
f.write(json_formatter.render())
245
246
# Summary for build logs
247
print(f"Analysis complete: {len(messages)} issues found")
248
if summary:
249
print(f"Time taken: {summary['time_taken']} seconds")
250
print(f"Tools run: {', '.join(summary['tools'])}")
251
252
generate_ci_reports()
253
```
254
255
### IDE Integration Examples
256
257
```python
258
from prospector.formatters import FORMATTERS
259
from prospector.config import ProspectorConfig
260
from prospector.run import Prospector
261
import subprocess
262
import json
263
264
def vscode_integration():
265
"""Generate VS Code compatible output"""
266
config = ProspectorConfig()
267
prospector = Prospector(config)
268
prospector.execute()
269
270
messages = prospector.get_messages()
271
summary = prospector.get_summary()
272
273
# VS Code format
274
vscode_formatter = FORMATTERS["vscode"](summary, messages, config.profile, None)
275
output = vscode_formatter.render()
276
277
# VS Code can parse this output when configured with appropriate problem matcher
278
print(output)
279
280
def emacs_integration():
281
"""Generate Emacs compilation mode compatible output"""
282
config = ProspectorConfig()
283
prospector = Prospector(config)
284
prospector.execute()
285
286
messages = prospector.get_messages()
287
summary = prospector.get_summary()
288
289
# Emacs format
290
emacs_formatter = FORMATTERS["emacs"](summary, messages, config.profile, None)
291
output = emacs_formatter.render()
292
293
# Emacs can jump to locations when this output is in compilation buffer
294
print(output)
295
296
# Run based on environment
297
import os
298
if os.environ.get("INSIDE_EMACS"):
299
emacs_integration()
300
elif os.environ.get("VSCODE_PID"):
301
vscode_integration()
302
```
303
304
### Custom Output Processing
305
306
```python
307
from prospector.formatters import FORMATTERS
308
from prospector.config import ProspectorConfig
309
from prospector.run import Prospector
310
import json
311
312
def analyze_by_tool():
313
"""Analyze results grouped by tool"""
314
config = ProspectorConfig()
315
prospector = Prospector(config)
316
prospector.execute()
317
318
messages = prospector.get_messages()
319
summary = prospector.get_summary()
320
321
# Get raw JSON data
322
json_formatter = FORMATTERS["json"](summary, messages, config.profile, None)
323
json_output = json_formatter.render()
324
data = json.loads(json_output)
325
326
# Group messages by tool
327
by_tool = {}
328
for message in data.get("messages", []):
329
tool = message["source"]
330
if tool not in by_tool:
331
by_tool[tool] = []
332
by_tool[tool].append(message)
333
334
# Report by tool
335
print("Issues by tool:")
336
for tool, tool_messages in by_tool.items():
337
print(f" {tool}: {len(tool_messages)} issues")
338
339
# Show most common issue types
340
codes = [msg["code"] for msg in tool_messages]
341
from collections import Counter
342
common_codes = Counter(codes).most_common(3)
343
for code, count in common_codes:
344
print(f" {code}: {count} occurrences")
345
346
analyze_by_tool()
347
```
348
349
### Custom Formatting
350
351
```python
352
from prospector.formatters.base import Formatter
353
from prospector.message import Message
354
from prospector.profiles.profile import ProspectorProfile
355
from pathlib import Path
356
from typing import Any, Optional
357
358
class CustomFormatter(Formatter):
359
"""Example custom formatter"""
360
361
def render(self, summary: bool = True, messages: bool = True, profile: bool = False) -> str:
362
output_lines = []
363
364
if summary and self.summary:
365
output_lines.append("=== ANALYSIS SUMMARY ===")
366
output_lines.append(f"Messages: {self.summary.get('message_count', 0)}")
367
output_lines.append(f"Time: {self.summary.get('time_taken', 'unknown')}")
368
output_lines.append("")
369
370
if messages:
371
output_lines.append("=== ISSUES ===")
372
for message in self.messages:
373
location = message.location
374
if location.path:
375
file_path = location.path
376
if self.relative_to:
377
try:
378
file_path = location.path.relative_to(self.relative_to)
379
except ValueError:
380
pass
381
382
line_info = f":{location.line}" if location.line else ""
383
output_lines.append(f"{file_path}{line_info} [{message.source}] {message.message}")
384
385
return "\n".join(output_lines)
386
387
# Usage
388
config = ProspectorConfig()
389
prospector = Prospector(config)
390
prospector.execute()
391
392
custom_formatter = CustomFormatter(
393
prospector.get_summary(),
394
prospector.get_messages(),
395
config.profile,
396
Path(".")
397
)
398
399
print(custom_formatter.render())
400
```