0
# Report Generation
1
2
REUSE provides comprehensive compliance reporting capabilities with multiple output formats. The reporting system generates detailed analysis of project compliance status, missing information, and file-by-file breakdowns.
3
4
## Capabilities
5
6
### Project Report Classes
7
8
Core report classes for comprehensive project analysis.
9
10
```python { .api }
11
class ProjectReport:
12
"""
13
Main project compliance report.
14
15
Contains comprehensive analysis of all files in a project,
16
including compliance status, missing information, and detailed
17
file-by-file breakdowns.
18
"""
19
20
def __init__(
21
self,
22
file_reports: list[FileReport],
23
missing_licenses: set[str],
24
unused_licenses: set[str],
25
read_errors: list[tuple[Path, Exception]]
26
):
27
"""
28
Initialize project report.
29
30
Args:
31
file_reports: List of individual file reports
32
missing_licenses: Set of referenced but missing license files
33
unused_licenses: Set of license files not referenced by any file
34
read_errors: List of files that couldn't be read with their errors
35
"""
36
37
class ProjectSubsetReport:
38
"""
39
Subset report for specific files.
40
41
Used when analyzing only a portion of the project files
42
rather than the complete project.
43
"""
44
45
def __init__(self, file_reports: list[FileReport]):
46
"""
47
Initialize subset report.
48
49
Args:
50
file_reports: List of file reports for the subset
51
"""
52
53
class FileReport:
54
"""
55
Report for individual file compliance.
56
57
Contains detailed information about a single file's
58
REUSE compliance status, including copyright, licensing,
59
and any issues found.
60
"""
61
62
def __init__(
63
self,
64
path: Path,
65
reuse_infos: list[ReuseInfo],
66
missing_copyright: bool,
67
missing_license: bool,
68
bad_licenses: set[str]
69
):
70
"""
71
Initialize file report.
72
73
Args:
74
path: File path being reported on
75
reuse_infos: List of REUSE information found for the file
76
missing_copyright: Whether file is missing copyright information
77
missing_license: Whether file is missing license information
78
bad_licenses: Set of invalid or problematic license identifiers
79
"""
80
```
81
82
### Report Protocol
83
84
Protocol definition for report implementations.
85
86
```python { .api }
87
class ProjectReportSubsetProtocol(Protocol):
88
"""
89
Protocol for report subset implementations.
90
91
Defines the interface that report classes must implement
92
to be compatible with formatting functions.
93
"""
94
95
file_reports: list[FileReport]
96
97
def is_compliant(self) -> bool:
98
"""Check if all files in the report are compliant."""
99
100
def non_compliant_files(self) -> list[FileReport]:
101
"""Get list of non-compliant files."""
102
103
def files_missing_copyright(self) -> list[FileReport]:
104
"""Get files missing copyright information."""
105
106
def files_missing_license(self) -> list[FileReport]:
107
"""Get files missing license information."""
108
```
109
110
### Plain Text Formatting
111
112
Format reports as human-readable plain text output.
113
114
```python { .api }
115
def format_plain(report: ProjectReport) -> str:
116
"""
117
Format report as plaintext for stdout output.
118
119
Args:
120
report: ProjectReport to format
121
122
Returns:
123
Multi-line string with human-readable report
124
125
Note:
126
Includes summary statistics, file-by-file analysis,
127
missing licenses, unused licenses, and overall compliance status.
128
"""
129
```
130
131
**Usage Examples:**
132
133
```python
134
from reuse.lint import format_plain
135
from reuse.project import Project
136
from reuse.report import ProjectReport
137
from pathlib import Path
138
139
# Generate project report (this would typically be done internally)
140
project = Project.from_directory(Path.cwd())
141
# ... report generation logic ...
142
143
# Format as plain text
144
plain_report = format_plain(project_report)
145
print(plain_report)
146
147
# Example output:
148
# ========================= SUMMARY =========================
149
#
150
# * Bad licenses: 0
151
# * Deprecated licenses: 0
152
# * Licenses without file extension: 0
153
# * Missing licenses: MIT
154
# * Unused licenses: 0
155
# * Used licenses: GPL-3.0-or-later
156
# * Read errors: 0
157
# * Files with copyright information: 15 / 20
158
# * Files with license information: 18 / 20
159
#
160
# ========================= MISSING COPYRIGHT ===============
161
#
162
# The following files have no copyright information:
163
# * src/utils.py
164
# * tests/test_helper.py
165
#
166
# ======================= MISSING LICENSES ==================
167
#
168
# The following files have no license information:
169
# * src/config.py
170
# * README.md
171
#
172
# Congratulations! Your project is compliant with version 3.3 of the REUSE Specification :-)
173
```
174
175
### JSON Formatting
176
177
Format reports as structured JSON for programmatic processing.
178
179
```python { .api }
180
def format_json(report: ProjectReport) -> str:
181
"""
182
Format report as JSON string.
183
184
Args:
185
report: ProjectReport to format
186
187
Returns:
188
JSON string with structured report data
189
190
Note:
191
Includes all report data in structured format suitable
192
for consumption by other tools and scripts.
193
"""
194
```
195
196
**Usage Examples:**
197
198
```python
199
from reuse.lint import format_json
200
import json
201
202
# Format as JSON
203
json_report = format_json(project_report)
204
report_data = json.loads(json_report)
205
206
print(f"Compliant: {report_data['summary']['compliant']}")
207
print(f"Total files: {report_data['summary']['file_count']}")
208
209
# Access detailed file information
210
for file_info in report_data['files']:
211
if not file_info['compliant']:
212
print(f"Non-compliant: {file_info['path']}")
213
if file_info['missing_copyright']:
214
print(" - Missing copyright")
215
if file_info['missing_license']:
216
print(" - Missing license")
217
218
# Example JSON structure:
219
# {
220
# "summary": {
221
# "compliant": false,
222
# "file_count": 20,
223
# "compliant_files": 15,
224
# "non_compliant_files": 5,
225
# "missing_licenses": ["MIT"],
226
# "unused_licenses": [],
227
# "bad_licenses": [],
228
# "read_errors": 0
229
# },
230
# "files": [
231
# {
232
# "path": "src/main.py",
233
# "compliant": true,
234
# "missing_copyright": false,
235
# "missing_license": false,
236
# "licenses": ["GPL-3.0-or-later"],
237
# "copyrights": ["2023 Jane Doe"]
238
# }
239
# ]
240
# }
241
```
242
243
### Line-based Formatting
244
245
Format reports as line-based output for processing by text tools.
246
247
```python { .api }
248
def format_lines_subset(report: ProjectReportSubsetProtocol) -> str:
249
"""
250
Format subset report as lines.
251
252
Args:
253
report: Report implementing ProjectReportSubsetProtocol
254
255
Returns:
256
Line-based string output with one item per line
257
"""
258
259
def format_lines(report: ProjectReport) -> str:
260
"""
261
Format full report as lines.
262
263
Args:
264
report: ProjectReport to format
265
266
Returns:
267
Line-based string output suitable for grep, awk, etc.
268
"""
269
```
270
271
**Usage Examples:**
272
273
```python
274
from reuse.lint import format_lines, format_lines_subset
275
276
# Format full report as lines
277
lines_report = format_lines(project_report)
278
print(lines_report)
279
280
# Example output:
281
# NON_COMPLIANT src/utils.py
282
# MISSING_COPYRIGHT src/utils.py
283
# MISSING_LICENSE src/config.py
284
# COMPLIANT src/main.py
285
# COMPLIANT tests/test_main.py
286
287
# Format subset report
288
subset_files = [report for report in project_report.file_reports[:5]]
289
subset_report = ProjectSubsetReport(subset_files)
290
subset_lines = format_lines_subset(subset_report)
291
print(subset_lines)
292
293
# Process with shell tools
294
# reuse lint --format=lines | grep "NON_COMPLIANT" | cut -d' ' -f2
295
```
296
297
## Report Analysis Functions
298
299
Functions for analyzing and extracting information from reports.
300
301
```python { .api }
302
# Report analysis methods (available on report classes)
303
def is_compliant(self) -> bool:
304
"""Check if the project/subset is fully compliant."""
305
306
def non_compliant_files(self) -> list[FileReport]:
307
"""Get list of files that are not compliant."""
308
309
def files_missing_copyright(self) -> list[FileReport]:
310
"""Get files missing copyright information."""
311
312
def files_missing_license(self) -> list[FileReport]:
313
"""Get files missing license information."""
314
315
def files_with_bad_licenses(self) -> list[FileReport]:
316
"""Get files with invalid license identifiers."""
317
```
318
319
**Usage Examples:**
320
321
```python
322
# Analyze project compliance
323
if project_report.is_compliant():
324
print("Project is fully REUSE compliant!")
325
else:
326
print("Project has compliance issues:")
327
328
# Show files missing copyright
329
missing_copyright = project_report.files_missing_copyright()
330
if missing_copyright:
331
print(f"\nFiles missing copyright ({len(missing_copyright)}):")
332
for file_report in missing_copyright:
333
print(f" - {file_report.path}")
334
335
# Show files missing license
336
missing_license = project_report.files_missing_license()
337
if missing_license:
338
print(f"\nFiles missing license ({len(missing_license)}):")
339
for file_report in missing_license:
340
print(f" - {file_report.path}")
341
342
# Show files with bad licenses
343
bad_license_files = project_report.files_with_bad_licenses()
344
if bad_license_files:
345
print(f"\nFiles with invalid licenses ({len(bad_license_files)}):")
346
for file_report in bad_license_files:
347
print(f" - {file_report.path}: {file_report.bad_licenses}")
348
```
349
350
## Custom Report Processing
351
352
Examples of custom report processing and analysis.
353
354
```python
355
from reuse.report import ProjectReport, FileReport
356
from reuse.project import Project
357
from pathlib import Path
358
import json
359
from typing import Dict, List
360
361
def generate_compliance_dashboard(project_path: Path) -> Dict:
362
"""Generate a compliance dashboard with detailed metrics."""
363
364
project = Project.from_directory(project_path)
365
# Note: In practice, you'd use the actual REUSE linting functionality
366
# This is a simplified example showing report structure usage
367
368
dashboard = {
369
"project_root": str(project_path),
370
"compliance_overview": {},
371
"file_categories": {
372
"compliant": [],
373
"missing_copyright": [],
374
"missing_license": [],
375
"bad_licenses": []
376
},
377
"license_analysis": {
378
"used_licenses": set(),
379
"missing_licenses": set(),
380
"unused_licenses": set()
381
},
382
"file_type_breakdown": {}
383
}
384
385
# This would typically use the actual report generation
386
# For demonstration, we'll show the structure
387
388
return dashboard
389
390
def export_compliance_report(report: ProjectReport, output_dir: Path) -> None:
391
"""Export compliance report in multiple formats."""
392
393
output_dir.mkdir(exist_ok=True)
394
395
# Plain text report
396
plain_path = output_dir / "compliance-report.txt"
397
with open(plain_path, 'w') as f:
398
f.write(format_plain(report))
399
400
# JSON report
401
json_path = output_dir / "compliance-report.json"
402
with open(json_path, 'w') as f:
403
f.write(format_json(report))
404
405
# Lines format for processing
406
lines_path = output_dir / "compliance-report.lines"
407
with open(lines_path, 'w') as f:
408
f.write(format_lines(report))
409
410
# Summary CSV
411
csv_path = output_dir / "compliance-summary.csv"
412
with open(csv_path, 'w') as f:
413
f.write("path,compliant,missing_copyright,missing_license,bad_licenses\n")
414
for file_report in report.file_reports:
415
compliant = not (file_report.missing_copyright or
416
file_report.missing_license or
417
file_report.bad_licenses)
418
bad_licenses_str = ";".join(file_report.bad_licenses)
419
f.write(f"{file_report.path},{compliant},"
420
f"{file_report.missing_copyright},"
421
f"{file_report.missing_license},"
422
f'"{bad_licenses_str}"\n')
423
424
print(f"Reports exported to {output_dir}")
425
426
# Usage
427
# dashboard = generate_compliance_dashboard(Path("/path/to/project"))
428
# export_compliance_report(project_report, Path("./reports"))
429
```
430
431
## Report Integration Examples
432
433
Examples of integrating REUSE reports with other tools and workflows.
434
435
```python
436
def ci_compliance_check(project_path: Path) -> bool:
437
"""CI/CD integration example for compliance checking."""
438
439
project = Project.from_directory(project_path)
440
# Generate report (simplified - would use actual REUSE linting)
441
442
# For CI integration, typically you'd:
443
# 1. Generate report
444
# 2. Check compliance status
445
# 3. Export results
446
# 4. Return exit code
447
448
# if not report.is_compliant():
449
# print("REUSE compliance check failed!")
450
# print(format_plain(report))
451
# return False
452
453
print("REUSE compliance check passed!")
454
return True
455
456
def generate_license_inventory(report: ProjectReport) -> Dict[str, List[str]]:
457
"""Generate license inventory from compliance report."""
458
459
inventory = {}
460
461
for file_report in report.file_reports:
462
for reuse_info in file_report.reuse_infos:
463
for license_expr in reuse_info.spdx_expressions:
464
license_id = str(license_expr)
465
if license_id not in inventory:
466
inventory[license_id] = []
467
inventory[license_id].append(str(file_report.path))
468
469
return inventory
470
471
# Usage in CI/CD pipelines:
472
# exit_code = 0 if ci_compliance_check(Path(".")) else 1
473
# sys.exit(exit_code)
474
```