Source code for logeverything.utils.path_utils
"""
Path utility functions for the LogEverything library.
"""
import os
from typing import Dict, Optional
# Cache for get_relative_path() with default base_path (most common hot path).
# Bounded to 500 entries to prevent unbounded growth; cleared on overflow.
_relative_path_cache: Dict[str, str] = {}
[docs]
def get_relative_path(absolute_path: str, base_path: Optional[str] = None) -> str:
"""
Convert an absolute path to a relative path for cleaner log output.
Args:
absolute_path: The absolute file path
base_path: Optional base path to calculate relative path from.
If None, uses current working directory.
Returns:
str: Relative path from base directory, or just filename if outside base
"""
# Fast path: check cache for default base_path case (most common in decorators)
if base_path is None:
cached = _relative_path_cache.get(absolute_path)
if cached is not None:
return cached
try:
# Convert to string if it's not already (handles Mock objects in tests)
if not isinstance(absolute_path, str):
absolute_path = str(absolute_path) # type: ignore[unreachable]
# Use provided base path or current working directory
if base_path is None:
base_path = os.getcwd()
# Try to get relative path from base directory
rel_path = os.path.relpath(absolute_path, base_path)
# If the relative path starts with many "../", just use the filename
if rel_path.startswith(".."):
result = os.path.basename(absolute_path)
else:
result = rel_path
except (ValueError, OSError, TypeError):
# Fallback to just the filename if relpath fails
result = os.path.basename(str(absolute_path))
# Cache the result for default base_path case
if base_path is None or base_path == os.getcwd():
if len(_relative_path_cache) > 500:
_relative_path_cache.clear()
_relative_path_cache[absolute_path] = result
return result
def get_filename_only(path: str) -> str:
"""
Get just the filename from a path (cross-platform).
Args:
path: The file path
Returns:
str: Just the filename part
"""
return os.path.basename(path)