"""File utility functions.""" import os from typing import Generator, Optional def find_files( pattern: str, base_path: Optional[str] = None, recursive: bool = True, ) -> Generator[str, None, None]: """Find files matching a pattern. Args: pattern: File pattern (e.g., "**/*.py") base_path: Base path to search from recursive: Whether to search recursively Yields: Matching file paths """ import glob search_pattern = pattern if base_path: search_pattern = os.path.join(base_path, pattern) matches = glob.glob(search_pattern, recursive=recursive) for match in sorted(matches): if os.path.isfile(match): yield match def read_file_safe(file_path: str, encoding: str = "utf-8") -> tuple[str, Optional[str]]: """Read file contents safely. Args: file_path: Path to file encoding: File encoding Returns: Tuple of (content, None) on success or (None, error_message) on failure """ try: with open(file_path, "r", encoding=encoding) as f: return f.read(), None except UnicodeDecodeError: try: with open(file_path, "r", encoding="latin-1") as f: return f.read(), None except Exception as e: return None, str(e) except Exception as e: return None, str(e) def get_file_extension(file_path: str) -> str: """Get file extension in lowercase. Args: file_path: Path to file Returns: File extension including the dot """ _, ext = os.path.splitext(file_path) return ext.lower() def ensure_directory_exists(path: str) -> None: """Ensure directory exists, create if needed. Args: path: Directory path """ os.makedirs(path, exist_ok=True) def get_relative_path(file_path: str, base_path: str) -> str: """Get relative path from base path. Args: file_path: Full file path base_path: Base path Returns: Relative path """ try: return os.path.relpath(file_path, base_path) except ValueError: return file_path