A tool for compliance with the REUSE recommendations for software licensing and copyright management.
npx @tessl/cli install tessl/pypi-reuse@5.1.0REUSE is a comprehensive Python tool for compliance with the REUSE recommendations developed by the Free Software Foundation Europe (FSFE). It simplifies copyright and licensing processes by offering automated tools to choose and provide licenses, add copyright and licensing information to files, and confirm REUSE compliance.
pip install reuseimport reuse
from reuse import ReuseInfo, SourceType
from reuse.project import Project, GlobalLicensingFound
from reuse.types import StrPathFor working with collections:
from typing import Collection, Optional, IteratorFor report generation:
from reuse.report import ProjectReport, FileReport
from reuse.lint import format_plain, format_json, format_lines, format_lines_subsetCLI usage:
reuse --helpfrom reuse.project import Project
from pathlib import Path
# Create a project instance
project = Project.from_directory(Path.cwd())
# Get REUSE information for a file (accepts StrPath)
reuse_info = project.reuse_info_of("src/example.py")
for info in reuse_info:
print(f"Licenses: {info.spdx_expressions}")
print(f"Copyright: {info.copyright_lines}")
# Iterate through all files in project
for file_path in project.all_files():
print(f"Processing: {file_path}")
# Get subset of files
files_to_check = ["src/main.py", "src/utils.py"]
for file_path in project.subset_files(files_to_check):
print(f"Checking: {file_path}")CLI usage:
# Lint project for REUSE compliance
reuse lint
# Add copyright and license to files
reuse annotate --copyright="2023 Jane Doe" --license="MIT" src/example.py
# Generate SPDX bill of materials
reuse spdx --format=json > project.spdx.json
# Download a license
reuse download MITThe REUSE tool is built around several key components:
Important: The Python API is documented but NOT guaranteed stable between minor or patch releases. Semantic versioning applies exclusively to the CLI command. Library users should pin to exact versions.
Complete CLI with 7 subcommands for REUSE compliance management including linting, annotation, SPDX generation, and license management.
reuse [OPTIONS] COMMAND [ARGS]...
# Global options:
--debug Enable debug statements
--suppress-deprecation Hide deprecation warnings
--include-submodules Do not skip Git submodules
--include-meson-subprojects Do not skip Meson subprojects
--no-multiprocessing Do not use multiprocessing
--root PATH Define project root directoryCore project discovery, configuration management, and file analysis capabilities.
class Project:
"""Central class holding project root and configuration."""
root: Path
include_submodules: bool = False
include_meson_subprojects: bool = False
vcs_strategy: VCSStrategy
global_licensing: Optional[GlobalLicensing] = None
license_map: dict[str, dict]
licenses: dict[str, Path]
licenses_without_extension: dict[str, Path]
@classmethod
def from_directory(
cls,
root: StrPath,
include_submodules: bool = False,
include_meson_subprojects: bool = False
) -> Project:
"""Factory method to create Project from directory.
Raises:
FileNotFoundError: if root does not exist.
NotADirectoryError: if root is not a directory.
UnicodeDecodeError: if the global licensing config file could not be decoded.
GlobalLicensingParseError: if the global licensing config file could not be parsed.
GlobalLicensingConflictError: if more than one global licensing config file is present.
"""
@classmethod
def find_global_licensing(
cls,
root: Path,
include_submodules: bool = False,
include_meson_subprojects: bool = False,
vcs_strategy: Optional[VCSStrategy] = None,
) -> list[GlobalLicensingFound]:
"""Find the path and corresponding class of a project directory's GlobalLicensing.
Raises:
GlobalLicensingConflictError: if more than one global licensing config file is present.
"""
def all_files(self, directory: Optional[StrPath] = None) -> Iterator[Path]:
"""Yield all files in directory and its subdirectories."""
def subset_files(
self, files: Collection[StrPath], directory: Optional[StrPath] = None
) -> Iterator[Path]:
"""Like all_files, but all files that are not in files are filtered out."""
def reuse_info_of(self, path: StrPath) -> list[ReuseInfo]:
"""Return REUSE info of path."""
def relative_from_root(self, path: StrPath) -> Path:
"""Return path relative to the project root."""Data structures and functions for processing REUSE licensing and copyright information.
@dataclass(frozen=True)
class ReuseInfo:
spdx_expressions: set[Expression] = field(default_factory=set)
copyright_lines: set[str] = field(default_factory=set)
contributor_lines: set[str] = field(default_factory=set)
path: Optional[str] = None
source_path: Optional[str] = None
source_type: Optional[SourceType] = None
def copy(self, **kwargs: Any) -> ReuseInfo: ...
def union(self, value: ReuseInfo) -> ReuseInfo: ...
def contains_copyright_or_licensing(self) -> bool: ...
class SourceType(Enum):
DOT_LICENSE = "dot-license"
FILE_HEADER = "file-header"
DEP5 = "dep5"
REUSE_TOML = "reuse-toml"Extensible comment processing system supporting 25+ file types for adding and parsing copyright headers.
def get_comment_style(path: Path) -> Optional[Type[CommentStyle]]: ...
def is_uncommentable(path: Path) -> bool: ...
def has_style(path: Path) -> bool: ...
class CommentStyle:
# Base class for comment style implementations
passSupport for project-wide licensing configuration through .reuse/dep5 and REUSE.toml files.
class GlobalLicensing(ABC):
# Abstract base class for global licensing
pass
class ReuseDep5(GlobalLicensing):
# Implementation for .reuse/dep5 files
pass
class ReuseTOML(GlobalLicensing):
# Implementation for REUSE.toml files
passComprehensive compliance reporting with multiple output formats including plain text, JSON, and line-based formats.
class ProjectReport:
"""Object that holds linting report about the project."""
# Main attributes
path: StrPath
licenses: dict[str, Path]
missing_licenses: dict[str, set[Path]]
bad_licenses: dict[str, set[Path]]
deprecated_licenses: set[str]
read_errors: set[Path]
file_reports: set[FileReport]
licenses_without_extension: dict[str, Path]
def __init__(self, do_checksum: bool = True): ...
# Properties
@property
def used_licenses(self) -> set[str]: ...
@property
def unused_licenses(self) -> set[str]: ...
@property
def files_without_licenses(self) -> set[Path]: ...
@property
def files_without_copyright(self) -> set[Path]: ...
@property
def is_compliant(self) -> bool: ...
class FileReport:
"""Report for individual file compliance."""
pass
# Formatting functions (from reuse.lint module)
def format_plain(report: ProjectReport) -> str:
"""Format report as plain text."""
def format_json(report: ProjectReport) -> str:
"""Format report as JSON."""
def format_lines(report: ProjectReport) -> str:
"""Format report as line-based output."""
def format_lines_subset(report: ProjectReportSubsetProtocol) -> str:
"""Format subset report as line-based output."""Pluggable version control system support for Git, Mercurial, Jujutsu, and Pijul.
class VCSStrategy(ABC):
# Abstract base class for VCS strategies
pass
def find_root(cwd: Optional[StrPath] = None) -> Optional[Path]: ...
def all_vcs_strategies() -> Generator[Type[VCSStrategy], None, None]: ...# Type definitions
StrPath = Union[str, PathLike[str]]
# Named tuples
class MultiLineSegments(NamedTuple):
start: str
middle: str
end: str
class GlobalLicensingFound(NamedTuple):
path: Path
cls: Type[GlobalLicensing]class ReuseError(Exception):
"""Base exception for all REUSE-specific errors"""
pass
class SpdxIdentifierNotFoundError(ReuseError):
"""Could not find SPDX identifier for license file"""
pass
class GlobalLicensingParseError(ReuseError):
"""Error parsing GlobalLicensing file"""
def __init__(self, *args, source: Optional[str] = None): ...
class GlobalLicensingParseTypeError(GlobalLicensingParseError, TypeError):
"""Type error while parsing a GlobalLicensing file"""
pass
class GlobalLicensingParseValueError(GlobalLicensingParseError, ValueError):
"""Value error while parsing a GlobalLicensing file"""
pass
class GlobalLicensingConflictError(ReuseError):
"""Two global licensing files in the project are not compatible"""
pass
class MissingReuseInfoError(ReuseError):
"""Missing REUSE information from result"""
pass
class CommentError(ReuseError):
"""Error during comment interaction"""
pass
class CommentCreateError(Exception):
"""Error occurred during the creation of a comment"""
pass
class CommentParseError(Exception):
"""Error occurred during the parsing of a comment"""
pass