This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user