#!/usr/bin/env python3 """ Path utility functions for PunimTag Ensures all paths are stored as absolute paths for consistency """ import os from pathlib import Path from typing import Union def normalize_path(path: Union[str, Path]) -> str: """ Convert any path to an absolute path. Args: path: Path to normalize (can be relative or absolute) Returns: Absolute path as string Examples: normalize_path("demo_photos") -> "/home/user/punimtag/demo_photos" normalize_path("./photos") -> "/home/user/punimtag/photos" normalize_path("/absolute/path") -> "/absolute/path" """ if not path: raise ValueError("Path cannot be empty or None") # Convert to string if Path object path_str = str(path) # Use pathlib for robust path resolution normalized = Path(path_str).resolve() return str(normalized) def is_absolute_path(path: Union[str, Path]) -> bool: """ Check if a path is absolute. Args: path: Path to check Returns: True if path is absolute, False if relative """ return Path(path).is_absolute() def is_relative_path(path: Union[str, Path]) -> bool: """ Check if a path is relative. Args: path: Path to check Returns: True if path is relative, False if absolute """ return not Path(path).is_absolute() def validate_path_exists(path: Union[str, Path]) -> bool: """ Check if a path exists and is accessible. Args: path: Path to validate Returns: True if path exists and is accessible, False otherwise """ try: normalized = normalize_path(path) return os.path.exists(normalized) and os.access(normalized, os.R_OK) except (ValueError, OSError): return False def get_path_info(path: Union[str, Path]) -> dict: """ Get detailed information about a path. Args: path: Path to analyze Returns: Dictionary with path information """ try: normalized = normalize_path(path) path_obj = Path(normalized) return { 'original': str(path), 'normalized': normalized, 'is_absolute': path_obj.is_absolute(), 'is_relative': not path_obj.is_absolute(), 'exists': path_obj.exists(), 'is_file': path_obj.is_file(), 'is_dir': path_obj.is_dir(), 'parent': str(path_obj.parent), 'name': path_obj.name, 'stem': path_obj.stem, 'suffix': path_obj.suffix } except Exception as e: return { 'original': str(path), 'error': str(e) } def ensure_directory_exists(path: Union[str, Path]) -> str: """ Ensure a directory exists, creating it if necessary. Args: path: Directory path to ensure exists Returns: Absolute path to the directory """ normalized = normalize_path(path) Path(normalized).mkdir(parents=True, exist_ok=True) return normalized def get_relative_path_from_base(absolute_path: Union[str, Path], base_path: Union[str, Path]) -> str: """ Get relative path from a base directory. Args: absolute_path: Absolute path to convert base_path: Base directory to make relative to Returns: Relative path from base directory """ abs_path = normalize_path(absolute_path) base = normalize_path(base_path) try: return str(Path(abs_path).relative_to(base)) except ValueError: # If paths don't share a common base, return the absolute path return abs_path # Test the utility functions if __name__ == "__main__": print("=== Path Utility Functions Test ===") test_paths = [ "demo_photos", "./demo_photos", "../demo_photos", "/home/ladmin/Code/punimtag/demo_photos", "C:/Users/Test/Photos", "/tmp/test" ] for path in test_paths: print(f"\nTesting: {path}") try: normalized = normalize_path(path) info = get_path_info(path) print(f" Normalized: {normalized}") print(f" Exists: {info['exists']}") print(f" Is directory: {info['is_dir']}") except Exception as e: print(f" Error: {e}")