from abc import ABC, abstractmethod from pathlib import Path import logging from typing import Set, Optional, Callable, Any from dataclasses import dataclass, field @dataclass class ProcessorOptions: """Options for file processing""" recursive: bool = True dry_run: bool = False debug: bool = False file_extensions: Set[str] = field(default_factory=lambda: {'.txt', '.caption'}) skip_hidden: bool = True class FileProcessor(ABC): """Base class for file processing operations""" def __init__(self, options: ProcessorOptions): self.options = options self._setup_logging() def _setup_logging(self) -> None: level = logging.DEBUG if self.options.debug else logging.INFO logging.basicConfig(level=level) self.logger = logging.getLogger(self.__class__.__name__) @abstractmethod def process_content(self, content: str) -> str: """Process the content of a single file""" pass def should_process_file(self, file_path: Path) -> bool: """Determine if a file should be processed based on options""" if self.options.skip_hidden and file_path.name.startswith('.'): return False return file_path.suffix.lower() in self.options.file_extensions def process_file(self, file_path: Path) -> None: """Process a single file""" try: self.logger.debug(f"Processing file: {file_path}") with open(file_path, 'r', encoding='utf-8') as f: content = f.read() processed_content = self.process_content(content) if self.options.dry_run: self.logger.info(f"Would write to {file_path}:") self.logger.info(processed_content) return with open(file_path, 'w', encoding='utf-8') as f: f.write(processed_content) self.logger.info(f"Successfully processed: {file_path}") except Exception as e: self.logger.error(f"Error processing {file_path}: {e}") def process_directory(self, directory: Path) -> None: """Process all matching files in a directory""" if not directory.is_dir(): raise NotADirectoryError(f"{directory} is not a directory") self.logger.info(f"Processing directory: {directory}") if self.options.recursive: files = directory.rglob('*') else: files = directory.glob('*') for file_path in files: if file_path.is_file() and self.should_process_file(file_path): self.process_file(file_path)