""" Backup Conversion System ======================== This module provides functionality for converting backups between different versions. Each version converter is implemented in a separate file and registered with the system. Usage: ------ To convert a backup from one version to another: ```python from misc.backup_converters import convert_backup # Convert a backup from version 1.0 to the latest version convert_backup(backup_dir, target_version=None) # None means latest version ``` Adding a New Converter: ---------------------- 1. Create a new file in the backup_converters directory, e.g., v1_to_v2.py 2. Implement a function that takes a backup directory and converts it to the target version 3. Register the converter in this file using the register_converter function Example: ```python # In v1_to_v2.py from misc.backup_converters import register_converter def convert_v1_to_v2(backup_dir): # Conversion logic here return new_version register_converter("1.0", "2.0", convert_v1_to_v2) ``` """ import os import json import logging from typing import Dict, Callable, Tuple, Optional, List logger = logging.getLogger(__name__) CURRENT_VERSION = "1.0" _converters: Dict[Tuple[str, str], Callable[[str], str]] = {} def register_converter(from_version: str, to_version: str, converter: Callable[[str], str]): """ Register a converter function for a specific version transition. Args: from_version (str): The source version to_version (str): The target version converter (callable): A function that takes a backup directory path and returns the new version """ _converters[(from_version, to_version)] = converter logger.info(f"Registered converter from version {from_version} to {to_version}") def get_backup_version(backup_dir: str) -> str: """ Get the version of a backup. Args: backup_dir (str): Path to the backup directory Returns: str: The version of the backup, or "0.0" if no version is found """ version_file = os.path.join(backup_dir, "version.json") if os.path.exists(version_file): try: with open(version_file, 'r') as f: version_data = json.load(f) return version_data.get("version", "0.0") except Exception as e: logger.error(f"Error reading version file: {e}") return "0.0" def set_backup_version(backup_dir: str, version: str): """ Set the version of a backup. Args: backup_dir (str): Path to the backup directory version (str): The version to set """ version_file = os.path.join(backup_dir, "version.json") try: with open(version_file, 'w') as f: json.dump({"version": version}, f) except Exception as e: logger.error(f"Error writing version file: {e}") def find_conversion_path(from_version: str, to_version: str) -> Optional[List[Tuple[str, str]]]: """ Find a path of converters to go from one version to another. Args: from_version (str): The source version to_version (str): The target version Returns: list: A list of (from, to) version pairs representing the conversion path, or None if no path is found """ if from_version == to_version: return [] if (from_version, to_version) in _converters: return [(from_version, to_version)] queue = [(from_version, [])] visited = {from_version} while queue: current, path = queue.pop(0) for (src, dst), _ in _converters.items(): if src == current and dst not in visited: new_path = path + [(src, dst)] if dst == to_version: return new_path visited.add(dst) queue.append((dst, new_path)) return None def convert_backup(backup_dir: str, target_version: Optional[str] = None) -> str: """ Convert a backup to the target version. Args: backup_dir (str): Path to the backup directory target_version (str, optional): The target version. If None, converts to the latest version. Returns: str: The new version of the backup Raises: ValueError: If no conversion path is found """ if target_version is None: target_version = CURRENT_VERSION current_version = get_backup_version(backup_dir) if current_version == target_version: return current_version path = find_conversion_path(current_version, target_version) if not path: raise ValueError(f"No conversion path found from version {current_version} to {target_version}") for from_ver, to_ver in path: converter = _converters.get((from_ver, to_ver)) if converter: logger.info(f"Converting backup from version {from_ver} to {to_ver}") new_version = converter(backup_dir) set_backup_version(backup_dir, new_version) else: raise ValueError(f"Converter not found for {from_ver} to {to_ver}") return target_version import pkgutil import importlib for _, name, is_pkg in pkgutil.iter_modules([os.path.dirname(__file__)]): if not is_pkg and name != "__init__": importlib.import_module(f"misc.backup_converters.{name}")