Source code for heterodyne.utils.path_validation
"""Path validation and filesystem utilities."""
from __future__ import annotations
import os
from pathlib import Path
[docs]
class PathValidationError(Exception):
"""Raised when path validation fails."""
[docs]
def resolve_path(path: str | Path) -> Path:
"""Resolve path to absolute, expanding user and symlinks.
Args:
path: Path string or Path object
Returns:
Resolved absolute Path
"""
return Path(path).expanduser().resolve()
[docs]
def validate_file_exists(path: str | Path, description: str = "File") -> Path:
"""Validate that a file exists and is readable.
Args:
path: Path to validate
description: Description for error messages
Returns:
Resolved Path object
Raises:
PathValidationError: If file doesn't exist or isn't readable
"""
resolved = resolve_path(path)
if not resolved.exists():
raise PathValidationError(f"{description} not found: {resolved}")
if not resolved.is_file():
raise PathValidationError(f"{description} is not a file: {resolved}")
if not os.access(resolved, os.R_OK):
raise PathValidationError(f"{description} is not readable: {resolved}")
return resolved
[docs]
def validate_output_path(path: str | Path, create_parents: bool = True) -> Path:
"""Validate and prepare output path.
Args:
path: Output path to validate
create_parents: Whether to create parent directories
Returns:
Resolved Path object
Raises:
PathValidationError: If path is invalid
"""
resolved = resolve_path(path)
if resolved.exists() and resolved.is_dir():
raise PathValidationError(f"Output path is a directory: {resolved}")
if create_parents:
resolved.parent.mkdir(parents=True, exist_ok=True)
elif not resolved.parent.exists():
raise PathValidationError(f"Parent directory does not exist: {resolved.parent}")
return resolved
[docs]
def ensure_directory(path: str | Path) -> Path:
"""Ensure directory exists, creating if necessary.
Args:
path: Directory path
Returns:
Resolved Path object
"""
resolved = resolve_path(path)
resolved.mkdir(parents=True, exist_ok=True)
return resolved