""" Cross-platform path utilities for py-libp2p. This module provides standardized path operations to ensure consistent behavior across Windows, macOS, and Linux platforms. """ import os from pathlib import Path import sys import tempfile from typing import Union PathLike = Union[str, Path] def get_temp_dir() -> Path: """ Get cross-platform temporary directory. Returns: Path: Platform-specific temporary directory path """ return Path(tempfile.gettempdir()) def get_project_root() -> Path: """ Get the project root directory. Returns: Path: Path to the py-libp2p project root """ # Navigate from libp2p/utils/paths.py to project root return Path(__file__).parent.parent.parent def join_paths(*parts: PathLike) -> Path: """ Cross-platform path joining. Args: *parts: Path components to join Returns: Path: Joined path using platform-appropriate separator """ return Path(*parts) def ensure_dir_exists(path: PathLike) -> Path: """ Ensure directory exists, create if needed. Args: path: Directory path to ensure exists Returns: Path: Path object for the directory """ path_obj = Path(path) path_obj.mkdir(parents=True, exist_ok=True) return path_obj def get_config_dir() -> Path: """ Get user config directory (cross-platform). Returns: Path: Platform-specific config directory """ if os.name == "nt": # Windows appdata = os.environ.get("APPDATA", "") if appdata: return Path(appdata) / "py-libp2p" else: # Fallback to user home directory return Path.home() / "AppData" / "Roaming" / "py-libp2p" else: # Unix-like (Linux, macOS) return Path.home() / ".config" / "py-libp2p" def get_script_dir(script_path: PathLike | None = None) -> Path: """ Get the directory containing a script file. Args: script_path: Path to the script file. If None, uses __file__ Returns: Path: Directory containing the script Raises: RuntimeError: If script path cannot be determined """ if script_path is None: # This will be the directory of the calling script import inspect frame = inspect.currentframe() if frame and frame.f_back: script_path = frame.f_back.f_globals.get("__file__") else: raise RuntimeError("Could not determine script path") if script_path is None: raise RuntimeError("Script path is None") return Path(script_path).parent.absolute() def create_temp_file(prefix: str = "py-libp2p_", suffix: str = ".log") -> Path: """ Create a temporary file with a unique name. Args: prefix: File name prefix suffix: File name suffix Returns: Path: Path to the created temporary file """ temp_dir = get_temp_dir() # Create a unique filename using timestamp and random bytes import secrets import time timestamp = time.strftime("%Y%m%d_%H%M%S") microseconds = f"{time.time() % 1:.6f}"[2:] # Get microseconds as string unique_id = secrets.token_hex(4) filename = f"{prefix}{timestamp}_{microseconds}_{unique_id}{suffix}" temp_file = temp_dir / filename # Create the file by touching it temp_file.touch() return temp_file def resolve_relative_path(base_path: PathLike, relative_path: PathLike) -> Path: """ Resolve a relative path from a base path. Args: base_path: Base directory path relative_path: Relative path to resolve Returns: Path: Resolved absolute path """ base = Path(base_path).resolve() relative = Path(relative_path) if relative.is_absolute(): return relative else: return (base / relative).resolve() def normalize_path(path: PathLike) -> Path: """ Normalize a path, resolving any symbolic links and relative components. Args: path: Path to normalize Returns: Path: Normalized absolute path """ return Path(path).resolve() def get_venv_path() -> Path | None: """ Get virtual environment path if active. Returns: Path: Virtual environment path if active, None otherwise """ venv_path = os.environ.get("VIRTUAL_ENV") if venv_path: return Path(venv_path) return None def get_python_executable() -> Path: """ Get current Python executable path. Returns: Path: Path to the current Python executable """ return Path(sys.executable) def find_executable(name: str) -> Path | None: """ Find executable in system PATH. Args: name: Name of the executable to find Returns: Path: Path to executable if found, None otherwise """ # Check if name already contains path if os.path.dirname(name): path = Path(name) if path.exists() and os.access(path, os.X_OK): return path return None # Search in PATH for path_dir in os.environ.get("PATH", "").split(os.pathsep): if not path_dir: continue path = Path(path_dir) / name if path.exists() and os.access(path, os.X_OK): return path return None def get_script_binary_path() -> Path: """ Get path to script's binary directory. Returns: Path: Directory containing the script's binary """ return get_python_executable().parent def get_binary_path(binary_name: str) -> Path | None: """ Find binary in PATH or virtual environment. Args: binary_name: Name of the binary to find Returns: Path: Path to binary if found, None otherwise """ # First check in virtual environment if active venv_path = get_venv_path() if venv_path: venv_bin = venv_path / "bin" if os.name != "nt" else venv_path / "Scripts" binary_path = venv_bin / binary_name if binary_path.exists() and os.access(binary_path, os.X_OK): return binary_path # Fall back to system PATH return find_executable(binary_name)