Source code for languagechange.cache

# Cache Manager module.Provides a CacheManager class for atomic file writes and automatic cache cleaning.

import os
import time
from contextlib import contextmanager
import filelock

DEFAULT_CACHE_DIR = "./cache"  # Default directory for cache files

[docs] class CacheManager: """ Manages cache files with atomic write operations to prevent data corruption in concurrent environments. The cache files are saved to a directory that can be specified during initialization. """ def __init__(self, cache_dir=None): """ Initializes the CacheManager with a directory to store cached files. Args: cache_dir (str, optional): The directory where cache files will be stored. Defaults to './cache' if not provided. """ self.cache_dir = os.path.abspath(cache_dir or DEFAULT_CACHE_DIR) os.makedirs(self.cache_dir, exist_ok=True)
[docs] @contextmanager def atomic_write(self, path): """ Provides a context manager for writing to cache files in an atomic way. This ensures that partial writes do not corrupt the target file, especially when multiple processes or threads access the same file. Args: path (str): The relative path to the cache file within the cache directory. Yields: file object: A writable file object for writing data. The file is temporary and will be renamed to the target file path after the write operation completes successfully. """ full_path = os.path.join(self.cache_dir, path) temp_path = full_path + ".tmp" # Temporary file path lock_path = full_path + ".lock" # Lock file path to prevent concurrent writes # Ensure the directory structure exists for the cache file os.makedirs(os.path.dirname(full_path), exist_ok=True) # Use a file lock to synchronize access across multiple processes or threads with filelock.FileLock(lock_path): try: # Step 1: Write data to a temporary file with open(temp_path, 'wb') as f: # Provide the temporary file handle to the caller for writing yield f # Step 2: Atomically replace the existing file with the new file if os.path.exists(full_path): os.remove(full_path) # Remove the existing file if it exists os.rename(temp_path, full_path) # Rename temp file to target file path finally: # Cleanup: Remove any leftover temporary or lock files for p in [temp_path, lock_path]: if os.path.exists(p): try: os.remove(p) except Exception: pass # Ignore errors during cleanup to avoid interference