Python library to parse, validate and create SPDX documents
—
CLI tools for parsing, validating, converting, and analyzing SPDX documents with support for all major formats and comprehensive error reporting.
Primary command-line interface for SPDX document operations including validation, conversion, and relationship graph generation.
def main(infile: str, outfile: str, version: str, novalidation: bool, graph: bool):
"""
CLI entry point for pyspdxtools command.
Provides comprehensive SPDX document processing including:
- Parsing documents in any supported format
- Validating against SPDX specifications
- Converting between formats
- Generating relationship graphs
Args:
infile: Input SPDX file path (required)
outfile: Output file path (optional, "-" for stdout)
version: SPDX version to validate against ("SPDX-2.2" or "SPDX-2.3")
novalidation: Skip validation if True
graph: Generate relationship graph if True (requires networkx, pygraphviz)
Exit Codes:
0: Success
1: Validation or processing error
"""Experimental command-line interface for SPDX 3.0 document operations.
def main() -> None:
"""
CLI entry point for pyspdxtools3 command.
Experimental CLI for SPDX 3.0 document processing.
Functionality and API subject to change.
"""Generate visual relationship graphs from SPDX documents.
def export_graph_from_document(document: Document, output_file: str) -> None:
"""
Generate relationship graph visualization from SPDX document.
Creates graphical representation of SPDX relationships showing
connections between packages, files, and other elements.
Args:
document: SPDX document to visualize
output_file: Output file path for graph image
Raises:
ImportError: If networkx or pygraphviz not installed
Note:
Requires optional dependencies: pip install "spdx-tools[graph_generation]"
"""# Validate SPDX document
pyspdxtools --infile document.spdx
# Convert between formats
pyspdxtools --infile document.spdx --outfile document.json
# Skip validation for faster conversion
pyspdxtools --infile document.spdx --outfile document.json --novalidation
# Validate against specific SPDX version
pyspdxtools --infile document.spdx --version SPDX-2.3
# Output to stdout (Tag-Value format)
pyspdxtools --infile document.json --outfile -
# Generate relationship graph
pyspdxtools --infile document.spdx --outfile relationships.png --graphimport sys
from spdx_tools.spdx.clitools.pyspdxtools import main
# Simulate command line arguments
sys.argv = [
"pyspdxtools",
"--infile", "input.spdx",
"--outfile", "output.json",
"--version", "SPDX-2.3"
]
try:
main()
print("CLI operation completed successfully")
except SystemExit as e:
if e.code == 0:
print("Success")
else:
print(f"Failed with exit code: {e.code}")import os
import subprocess
import sys
from pathlib import Path
def process_spdx_files(input_dir: str, output_dir: str, target_format: str):
"""Process all SPDX files in directory using CLI tool."""
input_path = Path(input_dir)
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
# Find all SPDX files
spdx_files = []
for ext in [".spdx", ".json", ".yaml", ".xml", ".rdf"]:
spdx_files.extend(input_path.glob(f"*{ext}"))
for input_file in spdx_files:
output_file = output_path / f"{input_file.stem}.{target_format}"
# Run pyspdxtools via subprocess
cmd = [
"pyspdxtools",
"--infile", str(input_file),
"--outfile", str(output_file)
]
try:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print(f"✅ Converted {input_file.name} -> {output_file.name}")
else:
print(f"❌ Failed to convert {input_file.name}")
print(f" Error: {result.stderr}")
except Exception as e:
print(f"❌ Error processing {input_file.name}: {e}")
# Usage
process_spdx_files("input_documents/", "output_documents/", "json")import subprocess
import json
from pathlib import Path
def validate_spdx_collection(directory: str) -> dict:
"""Validate all SPDX files in directory and return results."""
results = {
"total_files": 0,
"valid_files": 0,
"invalid_files": 0,
"errors": []
}
spdx_files = []
for ext in [".spdx", ".json", ".yaml", ".xml", ".rdf"]:
spdx_files.extend(Path(directory).glob(f"*{ext}"))
results["total_files"] = len(spdx_files)
for spdx_file in spdx_files:
cmd = ["pyspdxtools", "--infile", str(spdx_file)]
try:
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
results["valid_files"] += 1
print(f"✅ {spdx_file.name} is valid")
else:
results["invalid_files"] += 1
results["errors"].append({
"file": spdx_file.name,
"error": result.stderr.strip()
})
print(f"❌ {spdx_file.name} is invalid")
except Exception as e:
results["invalid_files"] += 1
results["errors"].append({
"file": spdx_file.name,
"error": str(e)
})
print(f"❌ Error validating {spdx_file.name}: {e}")
return results
# Generate validation report
results = validate_spdx_collection("spdx_documents/")
print(f"\nValidation Summary:")
print(f"Total files: {results['total_files']}")
print(f"Valid files: {results['valid_files']}")
print(f"Invalid files: {results['invalid_files']}")
# Save detailed report
with open("validation_report.json", "w") as f:
json.dump(results, f, indent=2)# Generate relationship graph
pyspdxtools --infile complex_project.spdx --outfile project_graph.png --graph
# Generate graph with specific format conversion
pyspdxtools --infile document.json --outfile graph.svg --graph# GitHub Actions example
name: SPDX Validation
on: [push, pull_request]
jobs:
validate-spdx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.9'
- name: Install spdx-tools
run: pip install spdx-tools
- name: Validate SPDX documents
run: |
for file in *.spdx *.json; do
if [ -f "$file" ]; then
echo "Validating $file..."
pyspdxtools --infile "$file" || exit 1
fi
done
- name: Convert to JSON for artifacts
run: |
for file in *.spdx; do
if [ -f "$file" ]; then
pyspdxtools --infile "$file" --outfile "${file%.*}.json"
fi
doneFROM python:3.9-slim
RUN pip install spdx-tools[graph_generation]
# Copy SPDX documents
COPY *.spdx /app/
WORKDIR /app
# Validate and convert documents
RUN for file in *.spdx; do \
pyspdxtools --infile "$file" --outfile "${file%.*}.json"; \
done
# Generate graphs
RUN for file in *.spdx; do \
pyspdxtools --infile "$file" --outfile "${file%.*}_graph.png" --graph; \
doneimport subprocess
import logging
def safe_spdx_operation(input_file: str, output_file: str = None,
validate: bool = True, graph: bool = False) -> bool:
"""Safely perform SPDX operations with comprehensive error handling."""
cmd = ["pyspdxtools", "--infile", input_file]
if output_file:
cmd.extend(["--outfile", output_file])
if not validate:
cmd.append("--novalidation")
if graph:
cmd.append("--graph")
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if result.returncode == 0:
logging.info(f"Successfully processed {input_file}")
if result.stdout:
logging.info(f"Output: {result.stdout}")
return True
else:
logging.error(f"Failed to process {input_file}")
logging.error(f"Error output: {result.stderr}")
return False
except subprocess.TimeoutExpired:
logging.error(f"Timeout processing {input_file}")
return False
except FileNotFoundError:
logging.error("pyspdxtools command not found. Is spdx-tools installed?")
return False
except Exception as e:
logging.error(f"Unexpected error processing {input_file}: {e}")
return False
# Usage
success = safe_spdx_operation("document.spdx", "document.json")
if success:
print("Operation completed successfully")
else:
print("Operation failed")from typing import Optional
# CLI functions work with file paths and configuration options
# Return values are typically None (success) or sys.exit() callsInstall with Tessl CLI
npx tessl i tessl/pypi-spdx-tools