172 lines
5.3 KiB
Python
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}") |