Files
HangmanLab.Backend/misc/backup_converters/__init__.py

172 lines
5.3 KiB
Python

"""
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}")