From ad200f4e3cc861aee054a3342d91676c696cefac Mon Sep 17 00:00:00 2001 From: Thomas Wilson Date: Mon, 6 Jan 2020 18:10:46 +0800 Subject: [PATCH] Find plugin interface and confspec --- shepherd/agent/plugin.py | 76 ++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/shepherd/agent/plugin.py b/shepherd/agent/plugin.py index 7b1e79a..46c2388 100644 --- a/shepherd/agent/plugin.py +++ b/shepherd/agent/plugin.py @@ -5,8 +5,10 @@ import logging import sys import pkgutil import pkg_resources +from configspec import ConfigSpecification from .. import base_plugins + log = logging.getLogger(__name__) @@ -35,8 +37,66 @@ def plugin_attachment(hookname): class PluginInterface(): + @staticmethod + def _load_interface(module, plugin_name) -> "PluginInterface": + """ + Finds PluginInterface instance in a plugin and returns it. + """ + def is_plugininterface(member): + return isinstance(member, PluginInterface) + + interface_list = inspect.getmembers(module, is_plugininterface) + if not interface_list: + raise PluginLoadError("Imported shepherd plugins must contain an instance" + " of PluginInterface") + + if len(interface_list) > 1: + log.warning(F"Plugin module {module.__name__} has more" + F" than one PluginInterface instance.") + + _, interface = interface_list[0] + interface._plugin_name = plugin_name + return interface + + def _load_confspec(self, module): + """ + If not already registered, looks for a ConfigSpecification instance in a plugin, + using a blank one by default. + """ + if self._confspec is not None: + return + + def is_confspec(member): + return isinstance(member, ConfigSpecification) + + confspec_list = inspect.getmembers(module, is_confspec) + if not confspec_list: + self._confspec = ConfigSpecification() + + if len(confspec_list) > 1: + log.warning(F"Plugin {self._plugin_name} has more" + F" than one root ConfigSpecification instance.") + + self.register_confspec(confspec_list[0][1]) + + def _load_pluginclass(self, module): + pass + def __init__(self): self._confspec = None + self._loaded = False + self._plugin_name = "" + + def _load_guard(self): + if self._loaded: + raise PluginLoadError("Cannot call interface register functions once" + " plugin is loaded") + + def register_confspec(self, confspec): + self._load_guard() + if not isinstance(confspec, ConfigSpecification): + raise PluginLoadError("confspec must be an instance of ConfigSpecification") + self._confspec = confspec @property def confspec(self): @@ -121,20 +181,8 @@ def load_plugin(plugin_name, plugin_dir=None): if not mod: raise PluginLoadError("Could not find plugin "+plugin_name) - # Scan imported module for PluginInterface instance - def is_plugininterface(member): - return isinstance(member, PluginInterface) - - interface_list = inspect.getmembers(mod, is_plugininterface) - if not interface_list: - raise PluginLoadError("Imported shepherd plugins must contain an instance" - " of PluginInterface") - - if len(interface_list) > 1: - log.warning(F"Plugin module {mod.__name__} has more" - F" than one PluginInterface instance.") - - _, interface = interface_list[0] + interface = PluginInterface._load_interface(mod, plugin_name) + interface._load_confspec(mod) # TODO Populate plugin interface