A tool for compliance with the REUSE recommendations for software licensing and copyright management.
—
REUSE provides an extensible comment processing system that supports 25+ file types for adding and parsing copyright headers. The system automatically detects comment styles and provides utilities for managing comment-based REUSE information.
Automatically detect and retrieve appropriate comment styles for files.
def get_comment_style(path: StrPath) -> Optional[Type[CommentStyle]]:
"""
Detect comment style for file path.
Args:
path: File path to analyze (accepts str or Path-like)
Returns:
CommentStyle class appropriate for the file type, or None if unsupported
Note:
Detection is based on file extension and content analysis.
Returns the most appropriate comment style class for the file.
"""
def is_uncommentable(path: Path) -> bool:
"""
Check if file cannot have comments added.
Args:
path: File path to check
Returns:
True if file type doesn't support comments (e.g., binary files)
"""
def has_style(path: Path) -> bool:
"""
Check if path has a known comment style.
Args:
path: File path to check
Returns:
True if a comment style exists for this file type
"""Usage Examples:
from reuse.comment import get_comment_style, is_uncommentable, has_style
from pathlib import Path
# Detect comment style for different file types
python_file = Path("example.py")
c_file = Path("example.c")
html_file = Path("example.html")
# Get comment styles
python_style = get_comment_style(python_file)
c_style = get_comment_style(c_file)
html_style = get_comment_style(html_file)
print(f"Python style: {python_style.__name__ if python_style else 'None'}")
print(f"C style: {c_style.__name__ if c_style else 'None'}")
print(f"HTML style: {html_style.__name__ if html_style else 'None'}")
# Check if files can be commented
print(f"Can comment Python: {not is_uncommentable(python_file)}")
print(f"Python has style: {has_style(python_file)}")
# Check binary file
binary_file = Path("example.png")
print(f"Can comment PNG: {not is_uncommentable(binary_file)}")The foundation class for all comment style implementations.
class CommentStyle:
"""
Base class for different file comment styles.
Subclasses implement specific comment syntax for different file types.
Each style defines how to create single-line and multi-line comments
appropriate for the file format.
"""
# Class attributes defined by subclasses:
SHORTHAND: str = "" # Short identifier for the style
SINGLE_LINE: str = "" # Single-line comment prefix
SINGLE_LINE_REGEXP: Optional[re.Pattern] = None # Regex for parsing single-line comments
INDENT_AFTER_SINGLE: str = "" # Indentation after single-line prefix
MULTI_LINE: MultiLineSegments = MultiLineSegments("", "", "") # Multi-line comment structure
INDENT_BEFORE_MIDDLE: str = "" # Indentation before middle marker
INDENT_AFTER_MIDDLE: str = "" # Indentation after middle marker
INDENT_BEFORE_END: str = "" # Indentation before end marker
SHEBANGS: list[str] = [] # Supported shebang patterns
@classmethod
def can_handle_single(cls) -> bool:
"""Whether the CommentStyle can handle single-line comments."""
@classmethod
def can_handle_multi(cls) -> bool:
"""Whether the CommentStyle can handle multi-line comments."""
@classmethod
def create_comment(cls, text: str, force_multi: bool = False) -> str:
"""Comment all lines in text. Single-line comments are preferred unless force_multi is True.
Raises:
CommentCreateError: if text could not be commented.
"""
@classmethod
def parse_comment(cls, text: str) -> str:
"""Parse and extract comment content from commented text.
Raises:
CommentParseError: if text could not be parsed.
"""
@classmethod
def comment_at_first_character(cls, text: str) -> str:
"""Create comment starting at the first character of text."""Data structure for multi-line comment formatting.
class MultiLineSegments(NamedTuple):
"""
Structure for multi-line comment formatting.
Attributes:
start: str - Start marker for multi-line comment
middle: str - Middle prefix for each line in multi-line comment
end: str - End marker for multi-line comment
"""
start: str
middle: str
end: strUsage Examples:
from reuse.comment import MultiLineSegments, CommentStyle
# Example multi-line segments for C-style comments
c_multiline = MultiLineSegments(
start="/*",
middle=" *",
end=" */"
)
print(f"C comment start: {c_multiline.start}")
print(f"C comment middle: {c_multiline.middle}")
print(f"C comment end: {c_multiline.end}")The system provides built-in support for 25+ file types through specialized comment style classes.
# Programming Languages
class PythonCommentStyle(CommentStyle):
"""Python files (.py) - # single-line comments"""
pass
class CCommentStyle(CommentStyle):
"""C files (.c, .h) - /* multi-line */ comments"""
pass
class CppCommentStyle(CommentStyle):
"""C++ files (.cpp, .hpp) - /* multi-line */ comments"""
pass
class CppSingleCommentStyle(CommentStyle):
"""C++ files - // single-line comments"""
pass
class JavaCommentStyle(CommentStyle):
"""Java files (.java) - /* multi-line */ and // single-line"""
pass
class HaskellCommentStyle(CommentStyle):
"""Haskell files (.hs) - -- single-line comments"""
pass
class JuliaCommentStyle(CommentStyle):
"""Julia files (.jl) - # single-line comments"""
pass
class FortranCommentStyle(CommentStyle):
"""Fortran files (.f, .for) - C or * in column 1"""
pass
class ModernFortranCommentStyle(CommentStyle):
"""Modern Fortran files (.f90, .f95) - ! single-line comments"""
pass
class LispCommentStyle(CommentStyle):
"""Lisp files (.lisp, .cl) - ; single-line comments"""
pass
class MlCommentStyle(CommentStyle):
"""ML files (.ml, .mli) - (* multi-line *) comments"""
pass
class LeanCommentStyle(CommentStyle):
"""Lean files (.lean) - -- single-line comments"""
pass
# Markup and Templates
class HtmlCommentStyle(CommentStyle):
"""HTML files (.html, .htm) - <!-- multi-line --> comments"""
pass
class JinjaCommentStyle(CommentStyle):
"""Jinja templates (.j2, .jinja) - {# multi-line #} comments"""
pass
class HandlebarsCommentStyle(CommentStyle):
"""Handlebars templates (.hbs) - {{! multi-line }} comments"""
pass
class VelocityCommentStyle(CommentStyle):
"""Velocity templates (.vm) - ## single-line comments"""
pass
class TexCommentStyle(CommentStyle):
"""TeX/LaTeX files (.tex, .sty) - % single-line comments"""
pass
class ReStructedTextCommentStyle(CommentStyle):
"""reStructuredText files (.rst) - .. single-line comments"""
pass
# Configuration and Data
class BibTexCommentStyle(CommentStyle):
"""BibTeX files (.bib) - % single-line comments"""
pass
class M4CommentStyle(CommentStyle):
"""M4 files (.m4) - dnl single-line comments"""
pass
class FtlCommentStyle(CommentStyle):
"""FTL files (.ftl) - <#-- multi-line --> comments"""
pass
# Scripting and System
class BatchFileCommentStyle(CommentStyle):
"""Batch files (.bat, .cmd) - REM single-line comments"""
pass
class AppleScriptCommentStyle(CommentStyle):
"""AppleScript files (.scpt) - -- single-line comments"""
pass
class VimCommentStyle(CommentStyle):
"""Vim script files (.vim) - \" single-line comments"""
pass
class UnixManCommentStyle(CommentStyle):
"""Unix manual pages (.1, .2, etc.) - .\\\" single-line comments"""
pass
# Other Formats
class AspxCommentStyle(CommentStyle):
"""ASPX files (.aspx) - <%-- multi-line --%> comments"""
pass
class XQueryCommentStyle(CommentStyle):
"""XQuery files (.xq, .xquery) - (: multi-line :) comments"""
pass
class PlantUmlCommentStyle(CommentStyle):
"""PlantUML files (.puml) - ' single-line comments"""
pass
class SemicolonCommentStyle(CommentStyle):
"""Files using semicolon comments - ; single-line comments"""
pass
# Special Cases
class EmptyCommentStyle(CommentStyle):
"""Files that support comments but with empty prefix"""
pass
class UncommentableCommentStyle(CommentStyle):
"""Files that cannot have comments (binary files, etc.)"""
passAccess the mapping between style names and comment style classes.
NAME_STYLE_MAP: dict[str, Type[CommentStyle]]
"""Dictionary mapping style names to comment style classes."""Usage Examples:
from reuse.comment import NAME_STYLE_MAP
# List all available comment styles
print("Available comment styles:")
for name, style_class in NAME_STYLE_MAP.items():
print(f" {name}: {style_class.__name__}")
# Get specific style by name
python_style = NAME_STYLE_MAP.get("python")
if python_style:
print(f"Python style class: {python_style.__name__}")
# Check if style exists
has_rust_style = "rust" in NAME_STYLE_MAP
print(f"Has Rust style: {has_rust_style}")from reuse.comment import get_comment_style, PythonCommentStyle, CCommentStyle
from pathlib import Path
def demonstrate_comment_styles():
"""Demonstrate comment style detection and usage."""
# File type detection
files = [
Path("example.py"),
Path("example.c"),
Path("example.html"),
Path("example.js"),
Path("example.tex")
]
for file_path in files:
style_class = get_comment_style(file_path)
if style_class:
print(f"\n{file_path.suffix} files use: {style_class.__name__}")
# Create style instance
style = style_class()
# Show single-line comment prefix
if hasattr(style, 'SINGLE_LINE') and style.SINGLE_LINE:
print(f" Single-line: '{style.SINGLE_LINE}'")
# Show multi-line comment structure
if hasattr(style, 'MULTI_LINE') and style.MULTI_LINE:
ml = style.MULTI_LINE
print(f" Multi-line: '{ml.start}' ... '{ml.middle}' ... '{ml.end}'")
demonstrate_comment_styles()from reuse.comment import get_comment_style, MultiLineSegments
from pathlib import Path
def create_comment_header(file_path: Path, copyright_text: str, license_id: str) -> str:
"""Create a properly formatted comment header for a file."""
style_class = get_comment_style(file_path)
if not style_class:
return ""
style = style_class()
# Prepare header content
header_lines = [
f"SPDX-FileCopyrightText: {copyright_text}",
f"SPDX-License-Identifier: {license_id}"
]
# Format based on comment style
if hasattr(style, 'MULTI_LINE') and style.MULTI_LINE:
# Use multi-line comment format
ml = style.MULTI_LINE
formatted_lines = [ml.start]
for line in header_lines:
formatted_lines.append(f"{ml.middle} {line}")
formatted_lines.append(ml.end)
return "\n".join(formatted_lines)
elif hasattr(style, 'SINGLE_LINE') and style.SINGLE_LINE:
# Use single-line comment format
prefix = style.SINGLE_LINE
indent = getattr(style, 'INDENT_AFTER_SINGLE', ' ')
formatted_lines = []
for line in header_lines:
formatted_lines.append(f"{prefix}{indent}{line}")
return "\n".join(formatted_lines)
return ""
# Usage examples
python_header = create_comment_header(
Path("example.py"),
"2023 Jane Doe <jane@example.com>",
"MIT"
)
print("Python header:")
print(python_header)
c_header = create_comment_header(
Path("example.c"),
"2023 Jane Doe <jane@example.com>",
"GPL-3.0-or-later"
)
print("\nC header:")
print(c_header)from reuse.comment import get_comment_style
from pathlib import Path
def show_comment_reference():
"""Show comment styles for common file types."""
file_extensions = [
".py", ".c", ".cpp", ".java", ".rs", ".go", ".js", ".ts",
".html", ".css", ".scss", ".php", ".rb", ".pl", ".sh",
".tex", ".md", ".rst", ".yaml", ".json", ".xml", ".sql"
]
print("File Type Comment Style Reference:")
print("=" * 50)
for ext in file_extensions:
test_file = Path(f"test{ext}")
style_class = get_comment_style(test_file)
if style_class:
style = style_class()
comment_info = []
if hasattr(style, 'SINGLE_LINE') and style.SINGLE_LINE:
comment_info.append(f"single: '{style.SINGLE_LINE}'")
if hasattr(style, 'MULTI_LINE') and style.MULTI_LINE:
ml = style.MULTI_LINE
comment_info.append(f"multi: '{ml.start}...{ml.end}'")
print(f"{ext:8} -> {style_class.__name__:25} ({', '.join(comment_info)})")
else:
print(f"{ext:8} -> No comment style available")
show_comment_reference()Install with Tessl CLI
npx tessl i tessl/pypi-reuse