applied formatting from yapf style file

This commit is contained in:
HERITAGE-XION 2026-01-10 19:58:35 +01:00
parent d0ab014b9d
commit 79d8b538f8
29 changed files with 294 additions and 171 deletions

5
.gitignore vendored
View File

@ -46,4 +46,7 @@ Thumbs.db
# ============== Project Specific ==============
# Output directory for processed images
tests/assets/output/
tests/assets/output/
.yapfignore/

4
.yapfignore Normal file
View File

@ -0,0 +1,4 @@
.venv/
venv/
env/
typings/

View File

@ -5,13 +5,14 @@ from pathlib import Path
from PIL import Image, PngImagePlugin
dest_dir = Path("tests/assets/test_images")
# Generate 45 PNG images with various metadata
for i in range(1, 46):
# Create a simple colored image
r, g, b = random.randint(50, 200), random.randint(50, 200), random.randint(50, 200)
img = Image.new("RGB", (100, 100), color=(r, g, b))
img = Image.new("RGB", (100, 100), color = (r, g, b))
# Add PNG text metadata
pnginfo = PngImagePlugin.PngInfo()
@ -23,7 +24,7 @@ for i in range(1, 46):
# Save with metadata
filename = dest_dir / f"generated_test_{i:02d}.png"
img.save(filename, pnginfo=pnginfo)
img.save(filename, pnginfo = pnginfo)
print("Generated 45 PNG images with metadata")
print(f"Total PNG count: {len(list(dest_dir.glob('*.png')))}")

View File

@ -15,6 +15,7 @@ from src.services.metadata_factory import MetadataFactory
from src.utils.display import print_metadata_table
from src.utils.get_target_files import get_target_files
console = Console()
log = logging.getLogger("metadata-scrubber")

View File

@ -27,6 +27,7 @@ from src.services.batch_processor import BatchProcessor
from src.utils.display import print_batch_summary
from src.utils.get_target_files import get_target_files
console = Console()
log = logging.getLogger("metadata-scrubber")
@ -93,7 +94,9 @@ def scrub(
# Show dry-run banner
if dry_run:
console.print("\n[bold yellow]🔍 DRY-RUN MODE[/bold yellow] - No files will be modified.\n")
console.print(
"\n[bold yellow]🔍 DRY-RUN MODE[/bold yellow] - No files will be modified.\n"
)
# Collect files to process
files = list(get_target_files(file_path, ext)) if recursive else [file_path]
@ -104,25 +107,31 @@ def scrub(
# Show worker count for batch operations
if len(files) > 1:
console.print(f"[dim]Processing {len(files)} files with {workers} workers...[/dim]\n")
console.print(
f"[dim]Processing {len(files)} files with {workers} workers...[/dim]\n"
)
# Initialize processor with worker count
processor = BatchProcessor(output_dir=output_dir, dry_run=dry_run, max_workers=workers)
processor = BatchProcessor(
output_dir = output_dir,
dry_run = dry_run,
max_workers = workers
)
# Process with thread-safe progress bar
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
BarColumn(),
MofNCompleteColumn(),
TaskProgressColumn(),
TimeElapsedColumn(),
console=console,
refresh_per_second=10, # Smooth updates for concurrent processing
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
BarColumn(),
MofNCompleteColumn(),
TaskProgressColumn(),
TimeElapsedColumn(),
console = console,
refresh_per_second = 10, # Smooth updates for concurrent processing
) as progress:
task_id = progress.add_task(
"[cyan]Scrubbing metadata...",
total=len(files),
total = len(files),
)
def on_file_complete(result):
@ -130,12 +139,12 @@ def scrub(
status = "" if result.success else ""
progress.update(
task_id,
description=f"[cyan]{status} {result.filepath.name}",
advance=1,
description = f"[cyan]{status} {result.filepath.name}",
advance = 1,
)
# Use concurrent batch processing
processor.process_batch(files, progress_callback=on_file_complete)
processor.process_batch(files, progress_callback = on_file_complete)
# Display summary
print_batch_summary(processor.get_summary())

View File

@ -14,26 +14,27 @@ from rich.console import Console
from src.services.metadata_factory import MetadataFactory
from src.services.report_generator import ReportGenerator
console = Console()
log = logging.getLogger("metadata-scrubber")
def verify(
original_path: Path = typer.Argument(
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
resolve_path=True,
help="Path to the original file (before scrubbing)",
exists = True,
file_okay = True,
dir_okay = False,
readable = True,
resolve_path = True,
help = "Path to the original file (before scrubbing)",
),
processed_path: Path = typer.Argument(
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
resolve_path=True,
help="Path to the processed file (after scrubbing)",
exists = True,
file_okay = True,
dir_okay = False,
readable = True,
resolve_path = True,
help = "Path to the processed file (after scrubbing)",
),
) -> None:
"""
@ -58,10 +59,10 @@ def verify(
# Generate comparison report
generator = ReportGenerator()
report = generator.generate_report(
original_file=str(original_path),
processed_file=str(processed_path),
before_metadata=before_metadata,
after_metadata=after_metadata,
original_file = str(original_path),
processed_file = str(processed_path),
before_metadata = before_metadata,
after_metadata = after_metadata,
)
# Render the report

View File

@ -34,7 +34,6 @@ class JpegProcessor:
tags_to_delete: List of EXIF tag IDs to remove.
data: Dict of extracted metadata with human-readable keys.
"""
def __init__(self):
"""Initialize the JPEG processor with empty data structures."""
self.tags_to_delete: list[int] = []
@ -69,15 +68,13 @@ class JpegProcessor:
# Get the human-readable name for the tag
tag_name = str(
tag_info.get("name", "Unknown Tag") if tag_info else "Unknown Tag"
tag_info.get("name",
"Unknown Tag") if tag_info else "Unknown Tag"
)
# Exclude tags necessary for image display integrity
if (
tag_name == "Orientation"
or tag_name == "ColorSpace"
or tag_name == "ExifTag"
):
if (tag_name == "Orientation" or tag_name == "ColorSpace"
or tag_name == "ExifTag"):
continue
# Save to list and dict

View File

@ -95,7 +95,7 @@ class PngProcessor:
found_metadata = True
if isinstance(value, bytes):
try:
value = value.decode("utf-8", errors="replace")
value = value.decode("utf-8", errors = "replace")
except Exception:
value = str(value)
@ -143,7 +143,9 @@ class PngProcessor:
raise MetadataProcessingError(f"Error processing PNG EXIF: {str(e)}")
def get_clean_pnginfo(
self, img: Image, keys_to_remove: list[str] | None = None
self,
img: Image,
keys_to_remove: list[str] | None = None
) -> PngImagePlugin.PngInfo | None:
"""
Create a new PngInfo with sensitive keys removed.

View File

@ -20,10 +20,9 @@ from src.commands.verify import verify
from src.utils.logger import setup_logging
# Initialize the Typer app with helpful defaults
app = typer.Typer(no_args_is_help=True, pretty_exceptions_show_locals=False)
app = typer.Typer(no_args_is_help = True, pretty_exceptions_show_locals = False)
log = logging.getLogger("metadata-scrubber")
__version__ = "0.3.0"
@ -71,9 +70,9 @@ def main(
# fmt: on
# register commands
app.command(name="read")(read)
app.command(name="scrub")(scrub)
app.command(name="verify")(verify)
app.command(name = "read")(read)
app.command(name = "scrub")(scrub)
app.command(name = "verify")(verify)
# run app
if __name__ == "__main__":

View File

@ -19,6 +19,7 @@ from rich.console import Console
from src.services.metadata_factory import MetadataFactory
log = logging.getLogger("metadata-scrubber")
console = Console()
@ -44,7 +45,7 @@ class BatchSummary:
failed: int = 0
dry_run: bool = False
output_dir: Path | None = None
results: list[FileResult] = field(default_factory=list)
results: list[FileResult] = field(default_factory = list)
class BatchProcessor:
@ -55,7 +56,6 @@ class BatchProcessor:
Supports dry-run mode, automatic duplicate suffix handling,
and concurrent processing via ThreadPoolExecutor.
"""
def __init__(
self,
output_dir: str | None = None,
@ -99,12 +99,12 @@ class BatchProcessor:
if self.dry_run:
# Verify the file can be handled (will raise if not)
MetadataFactory.get_handler(str(file))
output_path = self._get_unique_output_path(file, reserve=False)
output_path = self._get_unique_output_path(file, reserve = False)
result = FileResult(
filepath=file,
success=True,
action="dry-run",
output_path=output_path,
filepath = file,
success = True,
action = "dry-run",
output_path = output_path,
)
self._append_result(result)
log.debug(f"[DRY-RUN] Would process: {file}")
@ -121,10 +121,10 @@ class BatchProcessor:
handler.save(str(output_path))
result = FileResult(
filepath=file,
success=True,
action="scrubbed",
output_path=output_path,
filepath = file,
success = True,
action = "scrubbed",
output_path = output_path,
)
self._append_result(result)
if log.isEnabledFor(logging.DEBUG):
@ -137,10 +137,10 @@ class BatchProcessor:
self._cleanup_reserved_path(output_path)
result = FileResult(
filepath=file,
success=False,
action="skipped",
error=str(e),
filepath = file,
success = False,
action = "skipped",
error = str(e),
)
self._append_result(result)
if log.isEnabledFor(logging.DEBUG):
@ -151,7 +151,8 @@ class BatchProcessor:
def process_batch(
self,
files: Iterable[Path],
progress_callback: Callable[[FileResult], None] | None = None,
progress_callback: Callable[[FileResult],
None] | None = None,
) -> list[FileResult]:
"""
Process multiple files concurrently using ThreadPoolExecutor.
@ -170,10 +171,12 @@ class BatchProcessor:
return self.results
# Used ThreadPoolExecutor for I/O-bound concurrent processing
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
with ThreadPoolExecutor(max_workers = self.max_workers) as executor:
# Submit all files for processing
future_to_file = {
executor.submit(self.process_file, file): file for file in file_list
executor.submit(self.process_file,
file): file
for file in file_list
}
# Collect results as they complete
@ -209,14 +212,14 @@ class BatchProcessor:
results_copy = list(self.results)
summary = BatchSummary(
total=len(results_copy),
success=sum(
total = len(results_copy),
success = sum(
1 for r in results_copy if r.success and r.action == "scrubbed"
),
skipped=sum(1 for r in results_copy if not r.success),
dry_run=self.dry_run,
output_dir=self.output_dir,
results=results_copy,
skipped = sum(1 for r in results_copy if not r.success),
dry_run = self.dry_run,
output_dir = self.output_dir,
results = results_copy,
)
# Count dry-run as separate from success for clarity
if self.dry_run:
@ -266,7 +269,7 @@ class BatchProcessor:
"""
with self._path_lock:
# creates the destination directory if it doesn't exist
self.output_dir.mkdir(parents=True, exist_ok=True)
self.output_dir.mkdir(parents = True, exist_ok = True)
base_name = file.stem
extension = file.suffix

View File

@ -44,7 +44,6 @@ class ExcelHandler(MetadataHandler):
Attributes:
keys_to_delete: List of property names to be wiped.
"""
def __init__(self, filepath: str):
"""
Initialize the Excel handler.
@ -66,7 +65,7 @@ class ExcelHandler(MetadataHandler):
UnsupportedFormatError: If file extension is not a supported Excel format.
"""
ext = Path(self.filepath).suffix.lower()
normalised = FORMAT_MAP.get(ext[1:]) # Remove leading dot
normalised = FORMAT_MAP.get(ext[1 :]) # Remove leading dot
if normalised is None:
raise UnsupportedFormatError(f"Unsupported format: {ext}")
@ -152,7 +151,7 @@ class ExcelHandler(MetadataHandler):
# Use keep_vba=True for macro-enabled workbooks
detected_format = self._detect_format()
if detected_format == "xlsm":
wb = load_workbook(destination_file_path, keep_vba=True)
wb = load_workbook(destination_file_path, keep_vba = True)
else:
wb = load_workbook(destination_file_path)

View File

@ -39,7 +39,6 @@ class ImageHandler(MetadataHandler):
tags_to_delete: List of EXIF tags to remove during wipe operation.
detected_format: Actual image format detected by Pillow.
"""
def __init__(self, filepath: str):
"""
Initialize the image handler.
@ -48,10 +47,11 @@ class ImageHandler(MetadataHandler):
filepath: Path to the image file to process.
"""
super().__init__(filepath)
self.processors: dict[str, JpegProcessor | PngProcessor] = {
"jpeg": JpegProcessor(),
"png": PngProcessor(),
}
self.processors: dict[str,
JpegProcessor | PngProcessor] = {
"jpeg": JpegProcessor(),
"png": PngProcessor(),
}
self.tags_to_delete: list[int] = []
self.detected_format: str | None = None
self.text_keys_to_delete: list[str] = []
@ -128,12 +128,16 @@ class ImageHandler(MetadataHandler):
with Image.open(Path(self.filepath)) as img:
self.processed_metadata = cast(
dict[str, Any], processor.delete_metadata(img, self.tags_to_delete)
dict[str,
Any],
processor.delete_metadata(img,
self.tags_to_delete)
)
# For PNG, also get clean PngInfo
if isinstance(processor, PngProcessor):
self.clean_pnginfo = processor.get_clean_pnginfo(
img, self.text_keys_to_delete
img,
self.text_keys_to_delete
)
def save(self, output_path: str | Path | None = None) -> None:
@ -160,7 +164,7 @@ class ImageHandler(MetadataHandler):
shutil.copy2(self.filepath, destination_file_path)
with Image.open(destination_file_path) as img:
exif_bytes = piexif.dump(self.processed_metadata)
img.save(destination_file_path, exif=exif_bytes)
img.save(destination_file_path, exif = exif_bytes)
elif actual_format == "png":
# PNG: Open original, save fresh copy without metadata
@ -169,7 +173,9 @@ class ImageHandler(MetadataHandler):
# Preserve image mode and data integrity
img.save(
destination_file_path,
format="PNG",
exif=None,
pnginfo=getattr(self, "clean_pnginfo", None),
format = "PNG",
exif = None,
pnginfo = getattr(self,
"clean_pnginfo",
None),
)

View File

@ -28,7 +28,6 @@ class MetadataFactory:
- Images: .jpg, .jpeg, .png
- Future: .pdf, .docx, .xlsx, .pptx
"""
@staticmethod
def get_handler(filepath: str):
"""

View File

@ -23,7 +23,6 @@ class MetadataHandler(ABC):
metadata: Dictionary containing the extracted metadata.
processed_metadata: Dictionary containing metadata after processing.
"""
def __init__(self, filepath: str):
"""
Initialize the metadata handler.

View File

@ -33,7 +33,6 @@ class PDFHandler(MetadataHandler):
Attributes:
keys_to_delete: List of metadata keys to be wiped.
"""
def __init__(self, filepath: str):
"""
Initialize the PDF handler.
@ -58,7 +57,7 @@ class PDFHandler(MetadataHandler):
if ext != ".pdf":
raise UnsupportedFormatError(f"Unsupported format: {ext}")
return ext[1:] # Return 'pdf' without the dot
return ext[1 :] # Return 'pdf' without the dot
def read(self) -> dict[str, Any]:
"""

View File

@ -64,7 +64,6 @@ class PowerpointHandler(MetadataHandler):
Attributes:
keys_to_delete: List of property names to be wiped.
"""
def __init__(self, filepath: str):
"""
Initialize the PowerPoint handler.
@ -86,7 +85,7 @@ class PowerpointHandler(MetadataHandler):
UnsupportedFormatError: If file extension is not a supported PowerPoint format.
"""
ext = Path(self.filepath).suffix.lower()
normalised = FORMAT_MAP.get(ext[1:]) # Remove leading dot
normalised = FORMAT_MAP.get(ext[1 :]) # Remove leading dot
if normalised is None:
raise UnsupportedFormatError(f"Unsupported format: {ext}")

View File

@ -48,7 +48,7 @@ class ComparisonReport:
original_file: str
processed_file: str
comparisons: list[PropertyComparison] = field(default_factory=list)
comparisons: list[PropertyComparison] = field(default_factory = list)
status: VerificationStatus = VerificationStatus.CLEAN
removed_count: int = 0
preserved_count: int = 0
@ -80,8 +80,10 @@ class ReportGenerator:
def compare(
self,
before: dict[str, Any],
after: dict[str, Any],
before: dict[str,
Any],
after: dict[str,
Any],
preserved_keys: set[str] | None = None,
) -> ComparisonReport:
"""
@ -99,8 +101,8 @@ class ReportGenerator:
preserved_keys = self.PRESERVED_PROPERTIES
report = ComparisonReport(
original_file="",
processed_file="",
original_file = "",
processed_file = "",
)
# Get all unique keys from both dictionaries
@ -112,17 +114,20 @@ class ReportGenerator:
# Determine property status
status = self._determine_status(
key, before_value, after_value, preserved_keys
key,
before_value,
after_value,
preserved_keys
)
is_sensitive = key not in preserved_keys
comparison = PropertyComparison(
name=key,
before_value=before_value,
after_value=after_value,
status=status,
is_sensitive=is_sensitive,
name = key,
before_value = before_value,
after_value = after_value,
status = status,
is_sensitive = is_sensitive,
)
report.comparisons.append(comparison)
@ -214,14 +219,14 @@ class ReportGenerator:
}
table = Table(
title="[bold]Verification Report[/bold]",
show_header=True,
header_style="bold",
title = "[bold]Verification Report[/bold]",
show_header = True,
header_style = "bold",
)
table.add_column("Property", style="cyan")
table.add_column("Before", style="dim")
table.add_column("After", justify="left")
table.add_column("Property", style = "cyan")
table.add_column("Before", style = "dim")
table.add_column("After", justify = "left")
for comp in report.comparisons:
before_str = self._format_value(comp.before_value)
@ -248,7 +253,7 @@ class ReportGenerator:
return "[dim]None[/dim]"
if isinstance(value, str):
if len(value) > 30:
return value[:27] + "..."
return value[: 27] + "..."
return value if value.strip() else "[dim]-[/dim]"
return str(value)
@ -283,8 +288,10 @@ class ReportGenerator:
self,
original_file: str,
processed_file: str,
before_metadata: dict[str, Any],
after_metadata: dict[str, Any],
before_metadata: dict[str,
Any],
after_metadata: dict[str,
Any],
) -> ComparisonReport:
"""
Generate a full comparison report between two files.

View File

@ -61,7 +61,6 @@ class WorddocHandler(MetadataHandler):
Attributes:
keys_to_delete: List of property names to be wiped.
"""
def __init__(self, filepath: str):
"""
Initialize the Word document handler.
@ -83,7 +82,7 @@ class WorddocHandler(MetadataHandler):
UnsupportedFormatError: If file extension is not a supported Word format.
"""
ext = Path(self.filepath).suffix.lower()
normalised = FORMAT_MAP.get(ext[1:]) # Remove leading dot
normalised = FORMAT_MAP.get(ext[1 :]) # Remove leading dot
if normalised is None:
raise UnsupportedFormatError(f"Unsupported format: {ext}")

View File

@ -15,6 +15,7 @@ from rich.text import Text
from src.utils.formatter import clean_value
console = Console()
@ -37,7 +38,10 @@ def print_metadata_table(metadata: dict[str, Any]):
"/Author",
"/Creator",
],
"📸 Device Info": ["Make", "Model", "Software", "ExifVersion"],
"📸 Device Info": ["Make",
"Model",
"Software",
"ExifVersion"],
"⚙️ Exposure Settings": [
"ExposureTime",
"FNumber",
@ -68,9 +72,9 @@ def print_metadata_table(metadata: dict[str, Any]):
}
# Create the main table
table = Table(box=box.ROUNDED, show_header=True, header_style="bold magenta")
table.add_column("Property", style="cyan")
table.add_column("Value", style="green")
table = Table(box = box.ROUNDED, show_header = True, header_style = "bold magenta")
table.add_column("Property", style = "cyan")
table.add_column("Value", style = "green")
# Track which keys we have displayed to handle the "leftovers"
displayed_keys = set()
@ -82,7 +86,7 @@ def print_metadata_table(metadata: dict[str, Any]):
if section_data:
# Add a section row (acts as a sub-header)
table.add_row(Text(section_name, style="bold yellow"), "")
table.add_row(Text(section_name, style = "bold yellow"), "")
for key, val in section_data.items():
table.add_row(f" {key}", clean_value(val))
@ -98,13 +102,16 @@ def print_metadata_table(metadata: dict[str, Any]):
if k not in displayed_keys and k != "JPEGInterchangeFormat"
} # skip binary blobs
if leftovers:
table.add_row(Text("📝 Other", style="bold yellow"), "")
table.add_row(Text("📝 Other", style = "bold yellow"), "")
for key, val in leftovers.items():
table.add_row(f" {key}", clean_value(val))
# Print nicely inside a panel
console.print(
Panel(table, title="Metadata Report", border_style="blue", expand=False)
Panel(table,
title = "Metadata Report",
border_style = "blue",
expand = False)
)
@ -116,9 +123,9 @@ def print_batch_summary(summary) -> None:
summary: BatchSummary object with processing statistics.
"""
# Build the summary table
table = Table(box=box.ROUNDED, show_header=False, expand=False)
table.add_column("Metric", style="cyan")
table.add_column("Value", style="green")
table = Table(box = box.ROUNDED, show_header = False, expand = False)
table.add_column("Metric", style = "cyan")
table.add_column("Value", style = "green")
if summary.dry_run:
table.add_row("Mode", "[yellow]DRY-RUN (no changes made)[/yellow]")
@ -135,8 +142,8 @@ def print_batch_summary(summary) -> None:
failed = [r for r in summary.results if not r.success]
if failed:
table.add_section()
table.add_row(Text("Failed files:", style="bold red"), "")
for result in failed[:5]: # Show max 5 failures
table.add_row(Text("Failed files:", style = "bold red"), "")
for result in failed[: 5]: # Show max 5 failures
table.add_row(f" {result.filepath.name}", f"[dim]{result.error}[/dim]")
if len(failed) > 5:
table.add_row("", f"[dim]... and {len(failed) - 5} more[/dim]")
@ -152,4 +159,9 @@ def print_batch_summary(summary) -> None:
title = "⚠️ Scrub Complete (with warnings)"
border_style = "yellow"
console.print(Panel(table, title=title, border_style=border_style, expand=False))
console.print(
Panel(table,
title = title,
border_style = border_style,
expand = False)
)

View File

@ -32,14 +32,14 @@ def setup_logging(verbose: bool = False):
logging.getLogger().handlers.clear()
logging.basicConfig(
level=level,
format="%(message)s",
datefmt="[%X]",
handlers=[
level = level,
format = "%(message)s",
datefmt = "[%X]",
handlers = [
RichHandler(
rich_tracebacks=True, # Beautiful colorful stack traces
markup=True, # Allow [bold red] styles in logs
show_path=False, # Hide line number (cleaner for CLI tools)
rich_tracebacks = True, # Beautiful colorful stack traces
markup = True, # Allow [bold red] styles in logs
show_path = False, # Hide line number (cleaner for CLI tools)
)
],
)

View File

@ -22,6 +22,7 @@ from tests.conftest import (
get_xlsx_test_file,
)
runner = CliRunner()
# Test file paths (cross-platform)

View File

@ -22,6 +22,7 @@ from tests.conftest import (
get_xlsx_test_file,
)
runner = CliRunner()
# Test file paths (cross-platform)
@ -38,7 +39,7 @@ XLSX_DIR = get_test_xlsx_dir()
def output_dir(tmp_path):
"""Create isolated output directory for each test using tmp_path."""
output = tmp_path / "output"
output.mkdir(parents=True, exist_ok=True)
output.mkdir(parents = True, exist_ok = True)
return output
@ -58,7 +59,14 @@ def test_scrub_command_single_file_success(x, output_dir):
def test_scrub_command_recursive_jpg_success(output_dir):
"""Test the 'scrub' command with recursive directory processing for JPG."""
result = runner.invoke(
app, ["scrub", EXAMPLES_DIR, "-r", "-ext", "jpg", "--output", str(output_dir)]
app,
["scrub",
EXAMPLES_DIR,
"-r",
"-ext",
"jpg",
"--output",
str(output_dir)]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -69,7 +77,12 @@ def test_scrub_command_recursive_jpg_success(output_dir):
def test_scrub_command_dry_run(output_dir):
"""Test that --dry-run doesn't create files."""
result = runner.invoke(
app, ["scrub", JPG_TEST_FILE, "--output", str(output_dir), "--dry-run"]
app,
["scrub",
JPG_TEST_FILE,
"--output",
str(output_dir),
"--dry-run"]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -136,7 +149,14 @@ def test_scrub_command_pdf_single_file_success(output_dir):
def test_scrub_command_recursive_pdf_success(output_dir):
"""Test the 'scrub' command with recursive directory processing for PDF."""
result = runner.invoke(
app, ["scrub", PDF_DIR, "-r", "-ext", "pdf", "--output", str(output_dir)]
app,
["scrub",
PDF_DIR,
"-r",
"-ext",
"pdf",
"--output",
str(output_dir)]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -147,7 +167,12 @@ def test_scrub_command_recursive_pdf_success(output_dir):
def test_scrub_command_pdf_dry_run(output_dir):
"""Test that --dry-run doesn't create PDF files."""
result = runner.invoke(
app, ["scrub", PDF_TEST_FILE, "--output", str(output_dir), "--dry-run"]
app,
["scrub",
PDF_TEST_FILE,
"--output",
str(output_dir),
"--dry-run"]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -171,7 +196,14 @@ def test_scrub_command_xlsx_single_file_success(output_dir):
def test_scrub_command_recursive_xlsx_success(output_dir):
"""Test the 'scrub' command with recursive directory processing for Excel."""
result = runner.invoke(
app, ["scrub", XLSX_DIR, "-r", "-ext", "xlsx", "--output", str(output_dir)]
app,
["scrub",
XLSX_DIR,
"-r",
"-ext",
"xlsx",
"--output",
str(output_dir)]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -182,7 +214,12 @@ def test_scrub_command_recursive_xlsx_success(output_dir):
def test_scrub_command_xlsx_dry_run(output_dir):
"""Test that --dry-run doesn't create Excel files."""
result = runner.invoke(
app, ["scrub", XLSX_TEST_FILE, "--output", str(output_dir), "--dry-run"]
app,
["scrub",
XLSX_TEST_FILE,
"--output",
str(output_dir),
"--dry-run"]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -232,7 +269,14 @@ def test_scrub_command_recursive_pptx_success(output_dir):
PPTX_DIR = get_test_pptx_dir()
result = runner.invoke(
app, ["scrub", PPTX_DIR, "-r", "-ext", "pptx", "--output", str(output_dir)]
app,
["scrub",
PPTX_DIR,
"-r",
"-ext",
"pptx",
"--output",
str(output_dir)]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -246,7 +290,12 @@ def test_scrub_command_pptx_dry_run(output_dir):
PPTX_TEST_FILE = get_pptx_test_file()
result = runner.invoke(
app, ["scrub", PPTX_TEST_FILE, "--output", str(output_dir), "--dry-run"]
app,
["scrub",
PPTX_TEST_FILE,
"--output",
str(output_dir),
"--dry-run"]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -299,7 +348,14 @@ def test_scrub_command_recursive_docx_success(output_dir):
DOCX_DIR = get_test_docx_dir()
result = runner.invoke(
app, ["scrub", DOCX_DIR, "-r", "-ext", "docx", "--output", str(output_dir)]
app,
["scrub",
DOCX_DIR,
"-r",
"-ext",
"docx",
"--output",
str(output_dir)]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"
@ -313,7 +369,12 @@ def test_scrub_command_docx_dry_run(output_dir):
DOCX_TEST_FILE = get_docx_test_file()
result = runner.invoke(
app, ["scrub", DOCX_TEST_FILE, "--output", str(output_dir), "--dry-run"]
app,
["scrub",
DOCX_TEST_FILE,
"--output",
str(output_dir),
"--dry-run"]
)
assert result.exit_code == 0, f"Failed with: {result.stdout}"

View File

@ -13,6 +13,7 @@ from typer.testing import CliRunner
from src.main import app
runner = CliRunner()
@ -37,7 +38,11 @@ def test_verify_command_jpeg_clean_status(output_dir):
# First scrub the file
scrub_result = runner.invoke(
app, ["scrub", JPG_TEST_FILE, "--output", str(output_dir)]
app,
["scrub",
JPG_TEST_FILE,
"--output",
str(output_dir)]
)
assert scrub_result.exit_code == 0
@ -90,7 +95,11 @@ def test_verify_command_pdf_clean_status(output_dir):
# First scrub the file
scrub_result = runner.invoke(
app, ["scrub", PDF_TEST_FILE, "--output", str(output_dir)]
app,
["scrub",
PDF_TEST_FILE,
"--output",
str(output_dir)]
)
assert scrub_result.exit_code == 0
@ -116,7 +125,11 @@ def test_verify_command_xlsx_clean_status(output_dir):
# First scrub the file
scrub_result = runner.invoke(
app, ["scrub", XLSX_TEST_FILE, "--output", str(output_dir)]
app,
["scrub",
XLSX_TEST_FILE,
"--output",
str(output_dir)]
)
assert scrub_result.exit_code == 0
@ -142,7 +155,11 @@ def test_verify_command_pptx_clean_status(output_dir):
# First scrub the file
scrub_result = runner.invoke(
app, ["scrub", PPTX_TEST_FILE, "--output", str(output_dir)]
app,
["scrub",
PPTX_TEST_FILE,
"--output",
str(output_dir)]
)
assert scrub_result.exit_code == 0
@ -168,7 +185,11 @@ def test_verify_command_docx_clean_status(output_dir):
# First scrub the file
scrub_result = runner.invoke(
app, ["scrub", DOCX_TEST_FILE, "--output", str(output_dir)]
app,
["scrub",
DOCX_TEST_FILE,
"--output",
str(output_dir)]
)
assert scrub_result.exit_code == 0
@ -189,7 +210,10 @@ def test_verify_command_docx_clean_status(output_dir):
def test_verify_command_original_file_not_found():
"""Test verify command handles missing original file."""
result = runner.invoke(
app, ["verify", "nonexistent_file.jpg", "also_nonexistent.jpg"]
app,
["verify",
"nonexistent_file.jpg",
"also_nonexistent.jpg"]
)
assert result.exit_code != 0

View File

@ -46,8 +46,7 @@ def test_read_image_metadata(x):
if isinstance(handler, (JpegProcessor, PngProcessor)):
assert (
handler.tags_to_delete is not None
or handler.text_keys_to_delete is not None
handler.tags_to_delete is not None or handler.text_keys_to_delete is not None
)
@ -66,7 +65,7 @@ def test_wipe_image_metadata(x):
def test_save_processed_image_metadata(x):
"""Test saving processed image metadata through factory."""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = MetadataFactory.get_handler(str(x))
handler.read()
@ -116,7 +115,7 @@ def test_wipe_pdf_metadata_via_factory():
def test_save_processed_pdf_metadata_via_factory():
"""Test saving processed PDF metadata through MetadataFactory."""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = MetadataFactory.get_handler(PDF_TEST_FILE)
handler.read()
@ -163,7 +162,7 @@ def test_wipe_excel_metadata_via_factory():
def test_save_processed_excel_metadata_via_factory():
"""Test saving processed Excel metadata through MetadataFactory."""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = MetadataFactory.get_handler(XLSX_TEST_FILE)
handler.read()
@ -220,7 +219,7 @@ def test_save_processed_pptx_metadata_via_factory():
PPTX_TEST_FILE = get_pptx_test_file()
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = MetadataFactory.get_handler(PPTX_TEST_FILE)
handler.read()
@ -279,7 +278,7 @@ def test_save_processed_docx_metadata_via_factory():
DOCX_TEST_FILE = get_docx_test_file()
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = MetadataFactory.get_handler(DOCX_TEST_FILE)
handler.read()

View File

@ -68,7 +68,7 @@ def test_save_processed_excel_metadata(xlsx_file):
Test saving processed Excel to output path.
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = ExcelHandler(xlsx_file)
handler.read()
@ -99,7 +99,7 @@ def test_output_file_has_less_metadata(xlsx_file):
Test that the output file has metadata stripped.
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
# Process original file
handler = ExcelHandler(xlsx_file)

View File

@ -32,8 +32,7 @@ def test_read_image_metadata(x):
# checks if tags_to_delete or text_keys_to_delete is not empty
assert (
processor.tags_to_delete is not None
or processor.text_keys_to_delete is not None
processor.tags_to_delete is not None or processor.text_keys_to_delete is not None
)
# checks if metadata is a dictionary
@ -62,7 +61,7 @@ def test_save_processed_image_metadata(x):
"""
# creates output directory
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
processor = ImageHandler(x)
metadata = processor.read()
@ -116,7 +115,7 @@ def test_output_file_has_less_metadata(x):
Test that the output file has metadata stripped
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
# Process original file
processor = ImageHandler(x)

View File

@ -69,7 +69,7 @@ def test_save_processed_pdf_metadata(pdf_file):
Test saving processed PDF to output path.
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = PDFHandler(pdf_file)
handler.read()
@ -100,7 +100,7 @@ def test_output_file_has_less_metadata(pdf_file):
Test that the output file has metadata stripped.
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
# Process original file
handler = PDFHandler(pdf_file)

View File

@ -66,7 +66,7 @@ def test_save_processed_pptx_metadata(pptx_file):
Test saving processed PowerPoint to output path.
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = PowerpointHandler(pptx_file)
handler.read()
@ -97,7 +97,7 @@ def test_output_file_has_wiped_metadata(pptx_file):
Test that the output file has metadata wiped.
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
# Process original file
handler = PowerpointHandler(pptx_file)

View File

@ -66,7 +66,7 @@ def test_save_processed_docx_metadata(docx_file):
Test saving processed Word document to output path.
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
handler = WorddocHandler(docx_file)
handler.read()
@ -97,7 +97,7 @@ def test_output_file_has_wiped_metadata(docx_file):
Test that the output file has metadata wiped.
"""
output_dir = Path("./tests/assets/output")
output_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents = True, exist_ok = True)
# Process original file
handler = WorddocHandler(docx_file)