CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-reuse

A tool for compliance with the REUSE recommendations for software licensing and copyright management.

Pending
Overview
Eval results
Files

report-generation.mddocs/

Report Generation

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.

Capabilities

Project Report Classes

Core report classes for comprehensive project analysis.

class ProjectReport:
    """
    Main project compliance report.
    
    Contains comprehensive analysis of all files in a project,
    including compliance status, missing information, and detailed
    file-by-file breakdowns.
    """
    
    def __init__(
        self,
        file_reports: list[FileReport],
        missing_licenses: set[str],
        unused_licenses: set[str],
        read_errors: list[tuple[Path, Exception]]
    ):
        """
        Initialize project report.
        
        Args:
            file_reports: List of individual file reports
            missing_licenses: Set of referenced but missing license files
            unused_licenses: Set of license files not referenced by any file
            read_errors: List of files that couldn't be read with their errors
        """

class ProjectSubsetReport:
    """
    Subset report for specific files.
    
    Used when analyzing only a portion of the project files
    rather than the complete project.
    """
    
    def __init__(self, file_reports: list[FileReport]):
        """
        Initialize subset report.
        
        Args:
            file_reports: List of file reports for the subset
        """

class FileReport:
    """
    Report for individual file compliance.
    
    Contains detailed information about a single file's
    REUSE compliance status, including copyright, licensing,
    and any issues found.
    """
    
    def __init__(
        self,
        path: Path,
        reuse_infos: list[ReuseInfo],
        missing_copyright: bool,
        missing_license: bool,
        bad_licenses: set[str]
    ):
        """
        Initialize file report.
        
        Args:
            path: File path being reported on
            reuse_infos: List of REUSE information found for the file
            missing_copyright: Whether file is missing copyright information
            missing_license: Whether file is missing license information
            bad_licenses: Set of invalid or problematic license identifiers
        """

Report Protocol

Protocol definition for report implementations.

class ProjectReportSubsetProtocol(Protocol):
    """
    Protocol for report subset implementations.
    
    Defines the interface that report classes must implement
    to be compatible with formatting functions.
    """
    
    file_reports: list[FileReport]
    
    def is_compliant(self) -> bool:
        """Check if all files in the report are compliant."""
        
    def non_compliant_files(self) -> list[FileReport]:
        """Get list of non-compliant files."""
        
    def files_missing_copyright(self) -> list[FileReport]:
        """Get files missing copyright information."""
        
    def files_missing_license(self) -> list[FileReport]:
        """Get files missing license information."""

Plain Text Formatting

Format reports as human-readable plain text output.

def format_plain(report: ProjectReport) -> str:
    """
    Format report as plaintext for stdout output.
    
    Args:
        report: ProjectReport to format
        
    Returns:
        Multi-line string with human-readable report
        
    Note:
        Includes summary statistics, file-by-file analysis,
        missing licenses, unused licenses, and overall compliance status.
    """

Usage Examples:

from reuse.lint import format_plain
from reuse.project import Project
from reuse.report import ProjectReport
from pathlib import Path

# Generate project report (this would typically be done internally)
project = Project.from_directory(Path.cwd())
# ... report generation logic ...

# Format as plain text
plain_report = format_plain(project_report)
print(plain_report)

# Example output:
# ========================= SUMMARY =========================
# 
# * Bad licenses: 0
# * Deprecated licenses: 0
# * Licenses without file extension: 0
# * Missing licenses: MIT
# * Unused licenses: 0
# * Used licenses: GPL-3.0-or-later
# * Read errors: 0
# * Files with copyright information: 15 / 20
# * Files with license information: 18 / 20
# 
# ========================= MISSING COPYRIGHT ===============
# 
# The following files have no copyright information:
# * src/utils.py
# * tests/test_helper.py
# 
# ======================= MISSING LICENSES ==================
# 
# The following files have no license information:
# * src/config.py
# * README.md
# 
# Congratulations! Your project is compliant with version 3.3 of the REUSE Specification :-)

JSON Formatting

Format reports as structured JSON for programmatic processing.

def format_json(report: ProjectReport) -> str:
    """
    Format report as JSON string.
    
    Args:
        report: ProjectReport to format
        
    Returns:
        JSON string with structured report data
        
    Note:
        Includes all report data in structured format suitable
        for consumption by other tools and scripts.
    """

Usage Examples:

from reuse.lint import format_json
import json

# Format as JSON
json_report = format_json(project_report)
report_data = json.loads(json_report)

print(f"Compliant: {report_data['summary']['compliant']}")
print(f"Total files: {report_data['summary']['file_count']}")

# Access detailed file information
for file_info in report_data['files']:
    if not file_info['compliant']:
        print(f"Non-compliant: {file_info['path']}")
        if file_info['missing_copyright']:
            print("  - Missing copyright")
        if file_info['missing_license']:
            print("  - Missing license")

# Example JSON structure:
# {
#   "summary": {
#     "compliant": false,
#     "file_count": 20,
#     "compliant_files": 15,
#     "non_compliant_files": 5,
#     "missing_licenses": ["MIT"],
#     "unused_licenses": [],
#     "bad_licenses": [],
#     "read_errors": 0
#   },
#   "files": [
#     {
#       "path": "src/main.py",
#       "compliant": true,
#       "missing_copyright": false,
#       "missing_license": false,
#       "licenses": ["GPL-3.0-or-later"],
#       "copyrights": ["2023 Jane Doe"]
#     }
#   ]
# }

Line-based Formatting

Format reports as line-based output for processing by text tools.

def format_lines_subset(report: ProjectReportSubsetProtocol) -> str:
    """
    Format subset report as lines.
    
    Args:
        report: Report implementing ProjectReportSubsetProtocol
        
    Returns:
        Line-based string output with one item per line
    """

def format_lines(report: ProjectReport) -> str:
    """
    Format full report as lines.
    
    Args:
        report: ProjectReport to format
        
    Returns:
        Line-based string output suitable for grep, awk, etc.
    """

Usage Examples:

from reuse.lint import format_lines, format_lines_subset

# Format full report as lines
lines_report = format_lines(project_report)
print(lines_report)

# Example output:
# NON_COMPLIANT src/utils.py
# MISSING_COPYRIGHT src/utils.py
# MISSING_LICENSE src/config.py
# COMPLIANT src/main.py
# COMPLIANT tests/test_main.py

# Format subset report
subset_files = [report for report in project_report.file_reports[:5]]
subset_report = ProjectSubsetReport(subset_files)
subset_lines = format_lines_subset(subset_report)
print(subset_lines)

# Process with shell tools
# reuse lint --format=lines | grep "NON_COMPLIANT" | cut -d' ' -f2

Report Analysis Functions

Functions for analyzing and extracting information from reports.

# Report analysis methods (available on report classes)
def is_compliant(self) -> bool:
    """Check if the project/subset is fully compliant."""

def non_compliant_files(self) -> list[FileReport]:
    """Get list of files that are not compliant."""

def files_missing_copyright(self) -> list[FileReport]:
    """Get files missing copyright information."""

def files_missing_license(self) -> list[FileReport]:
    """Get files missing license information."""

def files_with_bad_licenses(self) -> list[FileReport]:
    """Get files with invalid license identifiers."""

Usage Examples:

# Analyze project compliance
if project_report.is_compliant():
    print("Project is fully REUSE compliant!")
else:
    print("Project has compliance issues:")
    
    # Show files missing copyright
    missing_copyright = project_report.files_missing_copyright()
    if missing_copyright:
        print(f"\nFiles missing copyright ({len(missing_copyright)}):")
        for file_report in missing_copyright:
            print(f"  - {file_report.path}")
    
    # Show files missing license
    missing_license = project_report.files_missing_license()
    if missing_license:
        print(f"\nFiles missing license ({len(missing_license)}):")
        for file_report in missing_license:
            print(f"  - {file_report.path}")
    
    # Show files with bad licenses
    bad_license_files = project_report.files_with_bad_licenses()
    if bad_license_files:
        print(f"\nFiles with invalid licenses ({len(bad_license_files)}):")
        for file_report in bad_license_files:
            print(f"  - {file_report.path}: {file_report.bad_licenses}")

Custom Report Processing

Examples of custom report processing and analysis.

from reuse.report import ProjectReport, FileReport
from reuse.project import Project
from pathlib import Path
import json
from typing import Dict, List

def generate_compliance_dashboard(project_path: Path) -> Dict:
    """Generate a compliance dashboard with detailed metrics."""
    
    project = Project.from_directory(project_path)
    # Note: In practice, you'd use the actual REUSE linting functionality
    # This is a simplified example showing report structure usage
    
    dashboard = {
        "project_root": str(project_path),
        "compliance_overview": {},
        "file_categories": {
            "compliant": [],
            "missing_copyright": [],
            "missing_license": [],
            "bad_licenses": []
        },
        "license_analysis": {
            "used_licenses": set(),
            "missing_licenses": set(),
            "unused_licenses": set()
        },
        "file_type_breakdown": {}
    }
    
    # This would typically use the actual report generation
    # For demonstration, we'll show the structure
    
    return dashboard

def export_compliance_report(report: ProjectReport, output_dir: Path) -> None:
    """Export compliance report in multiple formats."""
    
    output_dir.mkdir(exist_ok=True)
    
    # Plain text report
    plain_path = output_dir / "compliance-report.txt"
    with open(plain_path, 'w') as f:
        f.write(format_plain(report))
    
    # JSON report
    json_path = output_dir / "compliance-report.json"
    with open(json_path, 'w') as f:
        f.write(format_json(report))
    
    # Lines format for processing
    lines_path = output_dir / "compliance-report.lines"
    with open(lines_path, 'w') as f:
        f.write(format_lines(report))
    
    # Summary CSV
    csv_path = output_dir / "compliance-summary.csv"
    with open(csv_path, 'w') as f:
        f.write("path,compliant,missing_copyright,missing_license,bad_licenses\n")
        for file_report in report.file_reports:
            compliant = not (file_report.missing_copyright or 
                           file_report.missing_license or 
                           file_report.bad_licenses)
            bad_licenses_str = ";".join(file_report.bad_licenses)
            f.write(f"{file_report.path},{compliant},"
                   f"{file_report.missing_copyright},"
                   f"{file_report.missing_license},"
                   f'"{bad_licenses_str}"\n')
    
    print(f"Reports exported to {output_dir}")

# Usage
# dashboard = generate_compliance_dashboard(Path("/path/to/project"))
# export_compliance_report(project_report, Path("./reports"))

Report Integration Examples

Examples of integrating REUSE reports with other tools and workflows.

def ci_compliance_check(project_path: Path) -> bool:
    """CI/CD integration example for compliance checking."""
    
    project = Project.from_directory(project_path)
    # Generate report (simplified - would use actual REUSE linting)
    
    # For CI integration, typically you'd:
    # 1. Generate report
    # 2. Check compliance status
    # 3. Export results
    # 4. Return exit code
    
    # if not report.is_compliant():
    #     print("REUSE compliance check failed!")
    #     print(format_plain(report))
    #     return False
    
    print("REUSE compliance check passed!")
    return True

def generate_license_inventory(report: ProjectReport) -> Dict[str, List[str]]:
    """Generate license inventory from compliance report."""
    
    inventory = {}
    
    for file_report in report.file_reports:
        for reuse_info in file_report.reuse_infos:
            for license_expr in reuse_info.spdx_expressions:
                license_id = str(license_expr)
                if license_id not in inventory:
                    inventory[license_id] = []
                inventory[license_id].append(str(file_report.path))
    
    return inventory

# Usage in CI/CD pipelines:
# exit_code = 0 if ci_compliance_check(Path(".")) else 1
# sys.exit(exit_code)

Install with Tessl CLI

npx tessl i tessl/pypi-reuse

docs

cli.md

comment-handling.md

global-licensing.md

index.md

project-management.md

report-generation.md

reuse-info.md

vcs-integration.md

tile.json