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