import importlib from contextlib import contextmanager from functools import wraps from pathlib import Path from typing import Dict, List, Type import typer from rich.console import Console from utils.config import PatchTemplate from utils.tools import PATCHES class PatcherError(Exception): """Базовое исключение патчера""" pass class ConfigError(PatcherError): """Ошибка конфигурации""" pass class BuildError(PatcherError): """Ошибка сборки""" pass def handle_errors(func): """Декоратор для обработки ошибок CLI""" @wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except PatcherError as e: Console().print(f"[red]Ошибка: {e}") raise typer.Exit(1) except KeyboardInterrupt: Console().print("\n[yellow]Прервано пользователем") raise typer.Exit(130) return wrapper class PatchManager: """Менеджер для работы с патчами""" def __init__(self, console: Console, patches_dir: Path = PATCHES): self.console = console self.patches_dir = patches_dir def discover_patches(self, include_todo: bool = False) -> List[str]: """Находит все доступные патчи""" patches = [] for f in self.patches_dir.glob("*.py"): if f.name == "__init__.py": continue if f.name.startswith("todo_") and not include_todo: continue patches.append(f.stem) return patches def discover_all(self) -> Dict[str, List[str]]: """Находит все патчи, разделяя на готовые и в разработке""" ready = [] todo = [] for f in self.patches_dir.glob("*.py"): if f.name == "__init__.py": continue if f.name.startswith("todo_"): todo.append(f.stem) else: ready.append(f.stem) return {"ready": ready, "todo": todo} def load_patch_module(self, name: str) -> type: """Загружает модуль патча""" module = importlib.import_module(f"patches.{name}") return module def load_patch_class(self, name: str) -> type: """Загружает класс патча""" module = importlib.import_module(f"patches.{name}") return module.Patch def load_patch(self, name: str) -> PatchTemplate: """Загружает экземпляр патча""" module = importlib.import_module(f"patches.{name}") return module.Patch(name=name, console=self.console) def load_enabled_patches(self) -> List[PatchTemplate]: """Загружает все включённые патчи, отсортированные по приоритету""" patches = [] for name in self.discover_patches(): patch = self.load_patch(name) if patch.enabled: patches.append(patch) else: self.console.print(f"[dim]≫ Пропускаем {name}[/dim]") return sorted(patches, key=lambda p: p.priority, reverse=True)