pronoun-o-matic/plugin.py
2022-06-05 21:50:55 +02:00

62 lines
1.8 KiB
Python

from abc import abstractclassmethod, abstractmethod
from functools import cache
from importlib.metadata import entry_points
import logging
from typing import Any, Callable, List
from pronoun_update import PronounUpdate
class Plugin:
def __init__(self, config: dict[str, str]):
logging.info(f"Loading plugin {self.name()}")
self._load_from_config(config)
@abstractmethod
def _load_from_config(self, config: dict[str, Any]) -> None:
pass
@classmethod
@abstractmethod
def name(cls) -> str:
pass
@abstractmethod
def update_bio(self, update_pronouns: PronounUpdate) -> None:
pass
@cache
def load_plugins() -> dict[str, type[Plugin]]:
plugins: dict[str, type[Plugin]] = {}
# Ignore the following two lines due to incorrect type definition for entry_points
# See: https://github.com/python/typeshed/pull/7331
for plugin in entry_points(group="pronounomatic.plugins"): # type: ignore
pl = plugin.load() # type: ignore
assert issubclass(pl, Plugin)
plugins[pl.name()] = pl
return plugins
class InvalidPluginDefinitionException(Exception):
pass
def load_plugins_for_config(config: list[dict[str, Any]]) -> List[Plugin]:
plugins = []
plugin_classes = load_plugins()
for plugin_def in config:
if not plugin_def["name"]:
raise InvalidPluginDefinitionException("Missing plugin_name field")
plugin_name = plugin_def["name"]
if not plugin_name in load_plugins():
raise InvalidPluginDefinitionException(
f"No plugin with name {plugin_name} loaded"
)
plugin_cls = plugin_classes[plugin_name]
plugin = plugin_cls(plugin_def)
plugins.append(plugin)
return plugins