def available_importers(pm: PluginManager) -> Set[str]: plugins: Dict[str, Any] = dict(pm.list_name_plugin()) supported_file_types = { name[len(IMPORTER_PREFIX) :] for name in plugins if name.startswith(IMPORTER_PREFIX) } return supported_file_types
class SimplePluginManager(object): """ A PluginManager class for simple, non-scanning related plugins. """ def __init__(self, project_name, entrypoint, plugin_base_class): """ Initialize this plugin manager for the fully qualified Python module name `module_qname` with plugins loaded from the setuptools `entrypoint` that must subclass `plugin_base_class`. """ self.manager = PluggyPluginManager(project_name=project_name) self.entrypoint = entrypoint self.plugin_base_class = plugin_base_class self.manager.add_hookspecs(sys.modules[project_name]) # set to True once this manager is initialized by running its setup() self.initialized = False # mapping of {plugin.name: plugin_class} for all the loaded plugins of # this manager self.plugin_classes = OrderedDict() def setup(self): """ Load and validate available plugins for this PluginManager from its assigned `entrypoint`. Raise an Exception if a plugin is not valid such that when it does not subcclass the manager `plugin_base_class`. Must be called once to initialize the plugins if this manager. Return a list of all plugin classes for this manager. """ if self.initialized: return self.plugin_classes.values() entrypoint = self.entrypoint self.manager.load_setuptools_entrypoints(entrypoint) plugin_classes = [] for name, plugin_class in self.manager.list_name_plugin(): if not issubclass(plugin_class, self.plugin_base_class): plugin_base_class = self.plugin_base_class raise Exception( 'Invalid plugin: %(name)r: %(plugin_class)r ' 'must extend %(plugin_base_class)r.' % locals()) plugin_class.name = name plugin_classes.append(plugin_class) self.plugin_classes = OrderedDict([(cls.name, cls) for cls in plugin_classes]) self.initialized = True return self.plugin_classes.values()
def test_pm(pm: PluginManager) -> None: """Basic registration with objects""" class A: pass a1, a2 = A(), A() pm.register(a1) assert pm.is_registered(a1) pm.register(a2, "hello") assert pm.is_registered(a2) out = pm.get_plugins() assert a1 in out assert a2 in out assert pm.get_plugin("hello") == a2 assert pm.unregister(a1) == a1 assert not pm.is_registered(a1) out2 = pm.list_name_plugin() assert len(out2) == 1 assert out2 == [("hello", a2)]
def __init__(self, pluginmanager: PluginManager, file_type: str) -> None: self.file_type = file_type serializer_name = f"{SERIALIZER_PREFIX}{self.file_type}" importer_name = f"{IMPORTER_PREFIX}{self.file_type}" plugins = dict(pluginmanager.list_name_plugin()) if serializer_name not in plugins and importer_name not in plugins: options = available_serializers(pluginmanager).union( available_importers(pluginmanager) ) raise SerializationError( f"File type {file_type} not supported. You may" f" need to install a plugin supporting this type." f" Available types: {options}" ) self._entry_serializer = ( plugins[serializer_name] if serializer_name in plugins else plugins[importer_name] )
class PluginManager(object): """ A PluginManager class for scanning-related plugins. """ # a global managers cache as a mapping of {stage: manager instance} managers = {} def __init__(self, stage, module_qname, entrypoint, plugin_base_class): """ Initialize this plugin manager for the `stage` specified in the fully qualified Python module name `module_qname` with plugins loaded from the setuptools `entrypoint` that must subclass `plugin_base_class`. """ self.manager = PluggyPluginManager(project_name=stage) self.managers[stage] = self self.stage = stage self.entrypoint = entrypoint self.plugin_base_class = plugin_base_class self.manager.add_hookspecs(sys.modules[module_qname]) # set to True once this manager is initialized by running its setup() self.initialized = False # list of plugin_class for all the plugins of this manager self.plugin_classes = [] @classmethod def load_plugins(cls): """ Setup the plugins enviroment. Must be called once to initialize all the plugins of all managers. """ plugin_classes = [] plugin_options = [] for stage, manager in cls.managers.items(): mgr_setup = manager.setup() if not mgr_setup: msg = 'Cannot load plugins for stage: %(stage)s' % locals() raise PlugincodeError(msg) mplugin_classes, mplugin_options = mgr_setup plugin_classes.extend(mplugin_classes) plugin_options.extend(mplugin_options) return plugin_classes, plugin_options def setup(self): """ Return a tuple of (list of all plugin classes, list of all options of all plugin classes). Load and validate available plugins for this PluginManager from its assigned `entrypoint`. Raise a PlugincodeError if a plugin is not valid such that when it does not subcclass the manager `plugin_base_class`. Must be called once to setup the plugins of this manager. """ if self.initialized: return entrypoint = self.entrypoint try: self.manager.load_setuptools_entrypoints(entrypoint) except ImportError as e: raise e stage = self.stage plugin_options = [] plugin_classes = [] required_plugins = set() for name, plugin_class in self.manager.list_name_plugin(): if not issubclass(plugin_class, self.plugin_base_class): qname = '%(stage)s:%(name)s' % locals() plugin_base_class = self.plugin_base_class raise PlugincodeError( 'Invalid plugin: %(qname)r: %(plugin_class)r ' 'must extend %(plugin_base_class)r.' % locals()) for option in plugin_class.options: if not isinstance(option, cliutils.PluggableCommandLineOption): qname = '%(stage)s:%(name)s' % locals() oname = option.name clin = cliutils.PluggableCommandLineOption raise PlugincodeError( 'Invalid plugin: %(qname)r: option %(oname)r ' 'must extend %(clin)r.' % locals()) plugin_options.append(option) plugin_class.stage = stage plugin_class.name = name plugin_classes.append(plugin_class) self.plugin_classes = sorted(plugin_classes, key=lambda c: (c.sort_order, c.name)) self.initialized = True return self.plugin_classes, plugin_options