65 lines
2.1 KiB
Python
65 lines
2.1 KiB
Python
import pkg_resources
|
|
import logging
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
class PluginNotFoundError(Exception):
|
|
pass
|
|
|
|
|
|
class Plugin(object):
|
|
"""
|
|
Base class for a system that uses entry_points to load plugins.
|
|
|
|
Implementing classes are expected to have the following attributes:
|
|
|
|
entry_point: The name of the entry point to load plugins from
|
|
"""
|
|
|
|
_plugin_cache = None
|
|
|
|
@classmethod
|
|
def load_class(cls, identifier, default=None):
|
|
"""
|
|
Loads a single class instance specified by identifier. If identifier
|
|
specifies more than a single class, then logs a warning and returns the
|
|
first class identified.
|
|
|
|
If default is not None, will return default if no entry_point matching
|
|
identifier is found. Otherwise, will raise a ModuleMissingError
|
|
"""
|
|
if cls._plugin_cache is None:
|
|
cls._plugin_cache = {}
|
|
|
|
if identifier not in cls._plugin_cache:
|
|
identifier = identifier.lower()
|
|
classes = list(pkg_resources.iter_entry_points(
|
|
cls.entry_point, name=identifier))
|
|
|
|
if len(classes) > 1:
|
|
log.warning("Found multiple classes for {entry_point} with "
|
|
"identifier {id}: {classes}. "
|
|
"Returning the first one.".format(
|
|
entry_point=cls.entry_point,
|
|
id=identifier,
|
|
classes=", ".join(
|
|
class_.module_name for class_ in classes)))
|
|
|
|
if len(classes) == 0:
|
|
if default is not None:
|
|
return default
|
|
raise PluginNotFoundError(identifier)
|
|
|
|
cls._plugin_cache[identifier] = classes[0].load()
|
|
return cls._plugin_cache[identifier]
|
|
|
|
@classmethod
|
|
def load_classes(cls):
|
|
"""
|
|
Returns a list of containing the identifiers and their corresponding classes for all
|
|
of the available instances of this plugin
|
|
"""
|
|
return [(class_.name, class_.load())
|
|
for class_
|
|
in pkg_resources.iter_entry_points(cls.entry_point)]
|