71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
# -*- 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 <directory>/<submodule>.py als Teil des Pakets <package_alias>."""
|
|
load_package(directory, package_alias)
|
|
return importlib.import_module(f"{package_alias}.{submodule}")
|