# -*- coding: utf-8 -*- """ Trainer-Registry: Erkennung und Verwaltung von Trainern. Sucht Core-Trainer in trixy_core/trainer/core/*.py und Plugin-Trainer in plugins/*/trainer.py. """ from __future__ import annotations import importlib import importlib.util import inspect from pathlib import Path from trixy_core.trainer.itrainer import ITrainer from trixy_core.trainer.dataclasses import TrainerInfo from trixy_core.trainer.enums import TrainerType from trixy_core.utils.debug import pdebug, perror class TrainerRegistry: """Verwaltet alle verfuegbaren Trainer.""" def __init__(self) -> None: self._trainers: dict[str, ITrainer] = {} @property def trainers(self) -> dict[str, ITrainer]: """Alle registrierten Trainer.""" return dict(self._trainers) def register(self, trainer: ITrainer) -> None: """Registriert einen Trainer manuell.""" info = trainer.get_info() self._trainers[info.trainer_id] = trainer pdebug(f"Trainer registriert: {info.name} ({info.trainer_id})") def get(self, trainer_id: str) -> ITrainer | None: """Liefert einen Trainer nach ID.""" return self._trainers.get(trainer_id) def discover_core(self) -> int: """ Sucht Core-Trainer in trixy_core/trainer/core/. Returns: Anzahl gefundener Trainer """ core_dir = Path(__file__).parent / "core" if not core_dir.exists(): return 0 count = 0 # Flache .py Dateien for py_file in core_dir.glob("*.py"): if py_file.name.startswith("_"): continue try: module_name = f"trixy_core.trainer.core.{py_file.stem}" module = importlib.import_module(module_name) count += self._scan_module(module, TrainerType.CORE) except Exception as e: pdebug(f"Core-Trainer Fehler: {py_file.stem}: {e}") # Subdirectories mit trainer.py oder __init__.py for sub_dir in core_dir.iterdir(): if not sub_dir.is_dir() or sub_dir.name.startswith("_"): continue # trainer.py im Subdirectory suchen trainer_file = sub_dir / "trainer.py" if trainer_file.is_file(): try: module_name = f"trixy_core.trainer.core.{sub_dir.name}.trainer" module = importlib.import_module(module_name) found = self._scan_module(module, TrainerType.CORE) count += found if found: pdebug(f"Core-Trainer entdeckt: {sub_dir.name}/trainer.py") except Exception as e: pdebug(f"Core-Trainer Fehler: {sub_dir.name}: {e}") return count def discover_plugins(self, plugins_dir: str = "plugins") -> int: """ Sucht Plugin-Trainer in plugins/*/trainer.py. Returns: Anzahl gefundener Trainer """ base = Path(plugins_dir) if not base.exists(): return 0 count = 0 for trainer_file in base.glob("*/trainer.py"): try: spec = importlib.util.spec_from_file_location( f"plugin_trainer_{trainer_file.parent.name}", trainer_file, ) if spec and spec.loader: module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) count += self._scan_module(module, TrainerType.PLUGIN) except Exception as e: perror(f"Fehler beim Laden von Plugin-Trainer {trainer_file}: {e}") return count def _scan_module(self, module: object, trainer_type: TrainerType) -> int: """Scannt ein Modul nach ITrainer-Klassen und instanziiert sie.""" count = 0 for _, cls in inspect.getmembers(module, inspect.isclass): if ( issubclass(cls, ITrainer) and cls is not ITrainer and not inspect.isabstract(cls) ): try: instance = cls() info = instance.get_info() info.trainer_type = trainer_type self._trainers[info.trainer_id] = instance count += 1 pdebug(f"Trainer entdeckt: {info.name} ({trainer_type.value})") except Exception as e: perror(f"Fehler beim Instanziieren von {cls.__name__}: {e}") return count def list_info(self) -> list[TrainerInfo]: """Liefert Info-Objekte aller registrierten Trainer.""" result = [] for trainer in self._trainers.values(): result.append(trainer.get_info()) return result