# -*- coding: utf-8 -*- """ Import-Helfer für Plugins. Die eingebundenen Tools sind eigenständig gewachsen und benutzen deshalb naheliegende Modulnamen (app.py, storage.py, models.py …). Würden sie normal importiert, käme sich das zweite Tool mit dem ersten ins Gehege. Diese Helfer laden ein Modul bzw. ein Paket unter einem eindeutigen Namen direkt aus dem Plugin-Ordner. Die Tools selbst müssen dafür nichts ändern: paketinterne Importe (from .auth import ...) funktionieren unverändert. """ from __future__ import annotations import importlib import importlib.util import sys from pathlib import Path from types import ModuleType def load_module(file: Path, alias: str) -> ModuleType: """Lädt eine einzelne .py-Datei unter eindeutigem Namen.""" file = Path(file) if not file.is_file(): raise FileNotFoundError(f"{file} fehlt") if alias in sys.modules: return sys.modules[alias] spec = importlib.util.spec_from_file_location(alias, file) if spec is None or spec.loader is None: raise ImportError(f"{file} kann nicht geladen werden") module = importlib.util.module_from_spec(spec) sys.modules[alias] = module try: spec.loader.exec_module(module) except Exception: sys.modules.pop(alias, None) raise return module def load_package(directory: Path, alias: str) -> ModuleType: """Lädt ein Paket-Verzeichnis (mit __init__.py) unter eindeutigem Namen.""" directory = Path(directory) init = directory / "__init__.py" if not init.is_file(): raise FileNotFoundError(f"{init} fehlt") if alias in sys.modules: return sys.modules[alias] spec = importlib.util.spec_from_file_location( alias, init, submodule_search_locations=[str(directory)] ) if spec is None or spec.loader is None: raise ImportError(f"{directory} kann nicht geladen werden") package = importlib.util.module_from_spec(spec) sys.modules[alias] = package try: spec.loader.exec_module(package) except Exception: sys.modules.pop(alias, None) raise return package def load_submodule(directory: Path, package_alias: str, submodule: str) -> ModuleType: """Lädt /.py als Teil des Pakets .""" load_package(directory, package_alias) return importlib.import_module(f"{package_alias}.{submodule}")