Add utility modules: encryption, file_utils, git_utils, path_utils
This commit is contained in:
80
confsync/utils/path_utils.py
Normal file
80
confsync/utils/path_utils.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""Path utility functions for ConfSync."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
|
||||
def expand_path(path: str) -> str:
|
||||
"""Expand user home directory and environment variables in path."""
|
||||
return os.path.expandvars(os.path.expanduser(path))
|
||||
|
||||
|
||||
def normalize_path(path: str) -> str:
|
||||
"""Normalize a path to absolute form."""
|
||||
return str(Path(expand_path(path)).resolve())
|
||||
|
||||
|
||||
def get_home_directory() -> str:
|
||||
"""Get the user's home directory."""
|
||||
return str(Path.home())
|
||||
|
||||
|
||||
def get_xdg_config_dir() -> str:
|
||||
"""Get the XDG config directory, falling back to ~/.config."""
|
||||
xdg_config = os.environ.get('XDG_CONFIG_HOME')
|
||||
if xdg_config:
|
||||
return xdg_config
|
||||
return os.path.join(get_home_directory(), '.config')
|
||||
|
||||
|
||||
def get_xdg_data_dir() -> str:
|
||||
"""Get the XDG data directory, falling back to ~/.local/share."""
|
||||
xdg_data = os.environ.get('XDG_DATA_HOME')
|
||||
if xdg_data:
|
||||
return xdg_data
|
||||
return os.path.join(get_home_directory(), '.local', 'share')
|
||||
|
||||
|
||||
def list_directories(path: str) -> List[str]:
|
||||
"""List all directories in a path."""
|
||||
try:
|
||||
return [d for d in os.listdir(path) if os.path.isdir(os.path.join(path, d))]
|
||||
except OSError:
|
||||
return []
|
||||
|
||||
|
||||
def path_components(path: str) -> List[str]:
|
||||
"""Get all components of a path."""
|
||||
return list(Path(path).parts)
|
||||
|
||||
|
||||
def get_relative_path(path: str, base: str) -> str:
|
||||
"""Get the relative path from base to path."""
|
||||
try:
|
||||
return str(Path(path).relative_to(base))
|
||||
except ValueError:
|
||||
return path
|
||||
|
||||
|
||||
def is_subpath(path: str, parent: str) -> bool:
|
||||
"""Check if path is a subpath of parent."""
|
||||
try:
|
||||
path_resolved = Path(expand_path(path)).resolve()
|
||||
parent_resolved = Path(expand_path(parent)).resolve()
|
||||
return path_resolved == parent_resolved or parent_resolved in path_resolved.parents
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
|
||||
|
||||
def safe_join(base: str, *parts: str) -> str:
|
||||
"""Safely join paths, preventing directory traversal."""
|
||||
base_path = Path(expand_path(base)).resolve()
|
||||
|
||||
for part in parts:
|
||||
if part.startswith('/') or part.startswith('..'):
|
||||
if '..' in part:
|
||||
part = part.replace('..', '')
|
||||
base_path /= part
|
||||
|
||||
return str(base_path)
|
||||
Reference in New Issue
Block a user