diff --git a/autoProcessMedia.cfg.spec b/autoProcessMedia.cfg.spec index f6459900..d9264a78 100644 --- a/autoProcessMedia.cfg.spec +++ b/autoProcessMedia.cfg.spec @@ -32,11 +32,11 @@ [Posix] ### Process priority setting for External commands (Extractor and Transcoder) on Posix (Unix/Linux/OSX) systems. # Set the Niceness value for the nice command. These range from -20 (most favorable to the process) to 19 (least favorable to the process). - niceness = 10 + niceness = 0 # Set the ionice scheduling class. 0 for none, 1 for real time, 2 for best-effort, 3 for idle. - ionice_class = 2 + ionice_class = 0 # Set the ionice scheduling class data. This defines the class data, if the class accepts an argument. For real time and best-effort, 0-7 is valid data. - ionice_classdata = 4 + ionice_classdata = 0 [CouchPotato] #### autoProcessing for Movies diff --git a/libs/stevedore/__init__.py b/libs/stevedore/__init__.py new file mode 100755 index 00000000..93a56b2e --- /dev/null +++ b/libs/stevedore/__init__.py @@ -0,0 +1,36 @@ +# flake8: noqa + +__all__ = [ + 'ExtensionManager', + 'EnabledExtensionManager', + 'NamedExtensionManager', + 'HookManager', + 'DriverManager', +] + +from .extension import ExtensionManager +from .enabled import EnabledExtensionManager +from .named import NamedExtensionManager +from .hook import HookManager +from .driver import DriverManager + +import logging + +# Configure a NullHandler for our log messages in case +# the app we're used from does not set up logging. +LOG = logging.getLogger('stevedore') + +if hasattr(logging, 'NullHandler'): + LOG.addHandler(logging.NullHandler()) +else: + class NullHandler(logging.Handler): + def handle(self, record): + pass + + def emit(self, record): + pass + + def createLock(self): + self.lock = None + + LOG.addHandler(NullHandler()) diff --git a/libs/stevedore/dispatch.py b/libs/stevedore/dispatch.py new file mode 100755 index 00000000..226d3ae2 --- /dev/null +++ b/libs/stevedore/dispatch.py @@ -0,0 +1,216 @@ +import logging + +from .enabled import EnabledExtensionManager + +LOG = logging.getLogger(__name__) + + +class DispatchExtensionManager(EnabledExtensionManager): + """Loads all plugins and filters on execution. + + This is useful for long-running processes that need to pass + different inputs to different extensions. + + :param namespace: The namespace for the entry points. + :type namespace: str + :param check_func: Function to determine which extensions to load. + :type check_func: callable + :param invoke_on_load: Boolean controlling whether to invoke the + object returned by the entry point after the driver is loaded. + :type invoke_on_load: bool + :param invoke_args: Positional arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_args: tuple + :param invoke_kwds: Named arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_kwds: dict + :param propagate_map_exceptions: Boolean controlling whether exceptions + are propagated up through the map call or whether they are logged and + then ignored + :type invoke_on_load: bool + """ + + def map(self, filter_func, func, *args, **kwds): + """Iterate over the extensions invoking func() for any where + filter_func() returns True. + + The signature of filter_func() should be:: + + def filter_func(ext, *args, **kwds): + pass + + The first argument to filter_func(), 'ext', is the + :class:`~stevedore.extension.Extension` + instance. filter_func() should return True if the extension + should be invoked for the input arguments. + + The signature for func() should be:: + + def func(ext, *args, **kwds): + pass + + The first argument to func(), 'ext', is the + :class:`~stevedore.extension.Extension` instance. + + Exceptions raised from within func() are propagated up and + processing stopped if self.propagate_map_exceptions is True, + otherwise they are logged and ignored. + + :param filter_func: Callable to test each extension. + :param func: Callable to invoke for each extension. + :param args: Variable arguments to pass to func() + :param kwds: Keyword arguments to pass to func() + :returns: List of values returned from func() + """ + if not self.extensions: + # FIXME: Use a more specific exception class here. + raise RuntimeError('No %s extensions found' % self.namespace) + response = [] + for e in self.extensions: + if filter_func(e, *args, **kwds): + self._invoke_one_plugin(response.append, func, e, args, kwds) + return response + + def map_method(self, filter_func, method_name, *args, **kwds): + """Iterate over the extensions invoking each one's object method called + `method_name` for any where filter_func() returns True. + + This is equivalent of using :meth:`map` with func set to + `lambda x: x.obj.method_name()` + while being more convenient. + + Exceptions raised from within the called method are propagated up + and processing stopped if self.propagate_map_exceptions is True, + otherwise they are logged and ignored. + + .. versionadded:: 0.12 + + :param filter_func: Callable to test each extension. + :param method_name: The extension method name to call + for each extension. + :param args: Variable arguments to pass to method + :param kwds: Keyword arguments to pass to method + :returns: List of values returned from methods + """ + return self.map(filter_func, self._call_extension_method, + method_name, *args, **kwds) + + +class NameDispatchExtensionManager(DispatchExtensionManager): + """Loads all plugins and filters on execution. + + This is useful for long-running processes that need to pass + different inputs to different extensions and can predict the name + of the extensions before calling them. + + The check_func argument should return a boolean, with ``True`` + indicating that the extension should be loaded and made available + and ``False`` indicating that the extension should be ignored. + + :param namespace: The namespace for the entry points. + :type namespace: str + :param check_func: Function to determine which extensions to load. + :type check_func: callable + :param invoke_on_load: Boolean controlling whether to invoke the + object returned by the entry point after the driver is loaded. + :type invoke_on_load: bool + :param invoke_args: Positional arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_args: tuple + :param invoke_kwds: Named arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_kwds: dict + :param propagate_map_exceptions: Boolean controlling whether exceptions + are propagated up through the map call or whether they are logged and + then ignored + :type invoke_on_load: bool + :param on_load_failure_callback: Callback function that will be called when + a entrypoint can not be loaded. The arguments that will be provided + when this is called (when an entrypoint fails to load) are + (manager, entrypoint, exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + + """ + + def __init__(self, namespace, check_func, invoke_on_load=False, + invoke_args=(), invoke_kwds={}, + propagate_map_exceptions=False, + on_load_failure_callback=None, + verify_requirements=False): + super(NameDispatchExtensionManager, self).__init__( + namespace=namespace, + check_func=check_func, + invoke_on_load=invoke_on_load, + invoke_args=invoke_args, + invoke_kwds=invoke_kwds, + propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback, + verify_requirements=verify_requirements, + ) + + def _init_plugins(self, extensions): + super(NameDispatchExtensionManager, self)._init_plugins(extensions) + self.by_name = dict((e.name, e) for e in self.extensions) + + def map(self, names, func, *args, **kwds): + """Iterate over the extensions invoking func() for any where + the name is in the given list of names. + + The signature for func() should be:: + + def func(ext, *args, **kwds): + pass + + The first argument to func(), 'ext', is the + :class:`~stevedore.extension.Extension` instance. + + Exceptions raised from within func() are propagated up and + processing stopped if self.propagate_map_exceptions is True, + otherwise they are logged and ignored. + + :param names: List or set of name(s) of extension(s) to invoke. + :param func: Callable to invoke for each extension. + :param args: Variable arguments to pass to func() + :param kwds: Keyword arguments to pass to func() + :returns: List of values returned from func() + """ + response = [] + for name in names: + try: + e = self.by_name[name] + except KeyError: + LOG.debug('Missing extension %r being ignored', name) + else: + self._invoke_one_plugin(response.append, func, e, args, kwds) + return response + + def map_method(self, names, method_name, *args, **kwds): + """Iterate over the extensions invoking each one's object method called + `method_name` for any where the name is in the given list of names. + + This is equivalent of using :meth:`map` with func set to + `lambda x: x.obj.method_name()` + while being more convenient. + + Exceptions raised from within the called method are propagated up + and processing stopped if self.propagate_map_exceptions is True, + otherwise they are logged and ignored. + + .. versionadded:: 0.12 + + :param names: List or set of name(s) of extension(s) to invoke. + :param method_name: The extension method name + to call for each extension. + :param args: Variable arguments to pass to method + :param kwds: Keyword arguments to pass to method + :returns: List of values returned from methods + """ + return self.map(names, self._call_extension_method, + method_name, *args, **kwds) diff --git a/libs/stevedore/driver.py b/libs/stevedore/driver.py new file mode 100755 index 00000000..47273d06 --- /dev/null +++ b/libs/stevedore/driver.py @@ -0,0 +1,126 @@ +from .named import NamedExtensionManager + + +class DriverManager(NamedExtensionManager): + """Load a single plugin with a given name from the namespace. + + :param namespace: The namespace for the entry points. + :type namespace: str + :param name: The name of the driver to load. + :type name: str + :param invoke_on_load: Boolean controlling whether to invoke the + object returned by the entry point after the driver is loaded. + :type invoke_on_load: bool + :param invoke_args: Positional arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_args: tuple + :param invoke_kwds: Named arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_kwds: dict + :param on_load_failure_callback: Callback function that will be called when + a entrypoint can not be loaded. The arguments that will be provided + when this is called (when an entrypoint fails to load) are + (manager, entrypoint, exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + """ + + def __init__(self, namespace, name, + invoke_on_load=False, invoke_args=(), invoke_kwds={}, + on_load_failure_callback=None, + verify_requirements=False): + super(DriverManager, self).__init__( + namespace=namespace, + names=[name], + invoke_on_load=invoke_on_load, + invoke_args=invoke_args, + invoke_kwds=invoke_kwds, + on_load_failure_callback=on_load_failure_callback, + verify_requirements=verify_requirements, + ) + + @classmethod + def make_test_instance(cls, extension, namespace='TESTING', + propagate_map_exceptions=False, + on_load_failure_callback=None, + verify_requirements=False): + """Construct a test DriverManager + + Test instances are passed a list of extensions to work from rather + than loading them from entry points. + + :param extension: Pre-configured Extension instance + :type extension: :class:`~stevedore.extension.Extension` + :param namespace: The namespace for the manager; used only for + identification since the extensions are passed in. + :type namespace: str + :param propagate_map_exceptions: Boolean controlling whether exceptions + are propagated up through the map call or whether they are logged + and then ignored + :type propagate_map_exceptions: bool + :param on_load_failure_callback: Callback function that will + be called when a entrypoint can not be loaded. The + arguments that will be provided when this is called (when + an entrypoint fails to load) are (manager, entrypoint, + exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + :return: The manager instance, initialized for testing + + """ + + o = super(DriverManager, cls).make_test_instance( + [extension], namespace=namespace, + propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback, + verify_requirements=verify_requirements) + return o + + def _init_plugins(self, extensions): + super(DriverManager, self)._init_plugins(extensions) + + if not self.extensions: + name = self._names[0] + raise RuntimeError('No %r driver found, looking for %r' % + (self.namespace, name)) + if len(self.extensions) > 1: + discovered_drivers = ','.join(e.entry_point_target + for e in self.extensions) + + raise RuntimeError('Multiple %r drivers found: %s' % + (self.namespace, discovered_drivers)) + + def __call__(self, func, *args, **kwds): + """Invokes func() for the single loaded extension. + + The signature for func() should be:: + + def func(ext, *args, **kwds): + pass + + The first argument to func(), 'ext', is the + :class:`~stevedore.extension.Extension` instance. + + Exceptions raised from within func() are logged and ignored. + + :param func: Callable to invoke for each extension. + :param args: Variable arguments to pass to func() + :param kwds: Keyword arguments to pass to func() + :returns: List of values returned from func() + """ + results = self.map(func, *args, **kwds) + if results: + return results[0] + + @property + def driver(self): + """Returns the driver being used by this manager. + """ + ext = self.extensions[0] + return ext.obj if ext.obj else ext.plugin diff --git a/libs/stevedore/enabled.py b/libs/stevedore/enabled.py new file mode 100755 index 00000000..0d228db4 --- /dev/null +++ b/libs/stevedore/enabled.py @@ -0,0 +1,71 @@ +import logging + +from .extension import ExtensionManager + + +LOG = logging.getLogger(__name__) + + +class EnabledExtensionManager(ExtensionManager): + """Loads only plugins that pass a check function. + + The check_func argument should return a boolean, with ``True`` + indicating that the extension should be loaded and made available + and ``False`` indicating that the extension should be ignored. + + :param namespace: The namespace for the entry points. + :type namespace: str + :param check_func: Function to determine which extensions to load. + :type check_func: callable + :param invoke_on_load: Boolean controlling whether to invoke the + object returned by the entry point after the driver is loaded. + :type invoke_on_load: bool + :param invoke_args: Positional arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_args: tuple + :param invoke_kwds: Named arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_kwds: dict + :param propagate_map_exceptions: Boolean controlling whether exceptions + are propagated up through the map call or whether they are logged and + then ignored + :type propagate_map_exceptions: bool + :param on_load_failure_callback: Callback function that will be called when + a entrypoint can not be loaded. The arguments that will be provided + when this is called (when an entrypoint fails to load) are + (manager, entrypoint, exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + + """ + + def __init__(self, namespace, check_func, invoke_on_load=False, + invoke_args=(), invoke_kwds={}, + propagate_map_exceptions=False, + on_load_failure_callback=None, + verify_requirements=False,): + self.check_func = check_func + super(EnabledExtensionManager, self).__init__( + namespace, + invoke_on_load=invoke_on_load, + invoke_args=invoke_args, + invoke_kwds=invoke_kwds, + propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback, + verify_requirements=verify_requirements, + ) + + def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds, + verify_requirements): + ext = super(EnabledExtensionManager, self)._load_one_plugin( + ep, invoke_on_load, invoke_args, invoke_kwds, + verify_requirements, + ) + if ext and not self.check_func(ext): + LOG.debug('ignoring extension %r', ep.name) + return None + return ext diff --git a/libs/stevedore/extension.py b/libs/stevedore/extension.py new file mode 100755 index 00000000..2dd22c74 --- /dev/null +++ b/libs/stevedore/extension.py @@ -0,0 +1,275 @@ +"""ExtensionManager +""" + +import pkg_resources + +import logging + + +LOG = logging.getLogger(__name__) + + +class Extension(object): + """Book-keeping object for tracking extensions. + + The arguments passed to the constructor are saved as attributes of + the instance using the same names, and can be accessed by the + callables passed to :meth:`map` or when iterating over an + :class:`ExtensionManager` directly. + + :param name: The entry point name. + :type name: str + :param entry_point: The EntryPoint instance returned by + :mod:`pkg_resources`. + :type entry_point: EntryPoint + :param plugin: The value returned by entry_point.load() + :param obj: The object returned by ``plugin(*args, **kwds)`` if the + manager invoked the extension on load. + + """ + + def __init__(self, name, entry_point, plugin, obj): + self.name = name + self.entry_point = entry_point + self.plugin = plugin + self.obj = obj + + @property + def entry_point_target(self): + """The module and attribute referenced by this extension's entry_point. + + :return: A string representation of the target of the entry point in + 'dotted.module:object' format. + """ + return '%s:%s' % (self.entry_point.module_name, + self.entry_point.attrs[0]) + + +class ExtensionManager(object): + """Base class for all of the other managers. + + :param namespace: The namespace for the entry points. + :type namespace: str + :param invoke_on_load: Boolean controlling whether to invoke the + object returned by the entry point after the driver is loaded. + :type invoke_on_load: bool + :param invoke_args: Positional arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_args: tuple + :param invoke_kwds: Named arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_kwds: dict + :param propagate_map_exceptions: Boolean controlling whether exceptions + are propagated up through the map call or whether they are logged and + then ignored + :type propagate_map_exceptions: bool + :param on_load_failure_callback: Callback function that will be called when + a entrypoint can not be loaded. The arguments that will be provided + when this is called (when an entrypoint fails to load) are + (manager, entrypoint, exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + """ + + def __init__(self, namespace, + invoke_on_load=False, + invoke_args=(), + invoke_kwds={}, + propagate_map_exceptions=False, + on_load_failure_callback=None, + verify_requirements=False): + self._init_attributes( + namespace, + propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback) + extensions = self._load_plugins(invoke_on_load, + invoke_args, + invoke_kwds, + verify_requirements) + self._init_plugins(extensions) + + @classmethod + def make_test_instance(cls, extensions, namespace='TESTING', + propagate_map_exceptions=False, + on_load_failure_callback=None, + verify_requirements=False): + """Construct a test ExtensionManager + + Test instances are passed a list of extensions to work from rather + than loading them from entry points. + + :param extensions: Pre-configured Extension instances to use + :type extensions: list of :class:`~stevedore.extension.Extension` + :param namespace: The namespace for the manager; used only for + identification since the extensions are passed in. + :type namespace: str + :param propagate_map_exceptions: When calling map, controls whether + exceptions are propagated up through the map call or whether they + are logged and then ignored + :type propagate_map_exceptions: bool + :param on_load_failure_callback: Callback function that will + be called when a entrypoint can not be loaded. The + arguments that will be provided when this is called (when + an entrypoint fails to load) are (manager, entrypoint, + exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + :return: The manager instance, initialized for testing + + """ + + o = cls.__new__(cls) + o._init_attributes(namespace, + propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback) + o._init_plugins(extensions) + return o + + def _init_attributes(self, namespace, propagate_map_exceptions=False, + on_load_failure_callback=None): + self.namespace = namespace + self.propagate_map_exceptions = propagate_map_exceptions + self._on_load_failure_callback = on_load_failure_callback + + def _init_plugins(self, extensions): + self.extensions = extensions + self._extensions_by_name = None + + ENTRY_POINT_CACHE = {} + + def _find_entry_points(self, namespace): + if namespace not in self.ENTRY_POINT_CACHE: + eps = list(pkg_resources.iter_entry_points(namespace)) + self.ENTRY_POINT_CACHE[namespace] = eps + return self.ENTRY_POINT_CACHE[namespace] + + def _load_plugins(self, invoke_on_load, invoke_args, invoke_kwds, + verify_requirements): + extensions = [] + for ep in self._find_entry_points(self.namespace): + LOG.debug('found extension %r', ep) + try: + ext = self._load_one_plugin(ep, + invoke_on_load, + invoke_args, + invoke_kwds, + verify_requirements, + ) + if ext: + extensions.append(ext) + except (KeyboardInterrupt, AssertionError): + raise + except Exception as err: + LOG.error('Could not load %r: %s', ep.name, err) + LOG.exception(err) + if self._on_load_failure_callback is not None: + self._on_load_failure_callback(self, ep, err) + return extensions + + def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds, + verify_requirements): + plugin = ep.load(require=verify_requirements) + if invoke_on_load: + obj = plugin(*invoke_args, **invoke_kwds) + else: + obj = None + return Extension(ep.name, ep, plugin, obj) + + def names(self): + "Returns the names of the discovered extensions" + # We want to return the names of the extensions in the order + # they would be used by map(), since some subclasses change + # that order. + return [e.name for e in self.extensions] + + def map(self, func, *args, **kwds): + """Iterate over the extensions invoking func() for each. + + The signature for func() should be:: + + def func(ext, *args, **kwds): + pass + + The first argument to func(), 'ext', is the + :class:`~stevedore.extension.Extension` instance. + + Exceptions raised from within func() are propagated up and + processing stopped if self.propagate_map_exceptions is True, + otherwise they are logged and ignored. + + :param func: Callable to invoke for each extension. + :param args: Variable arguments to pass to func() + :param kwds: Keyword arguments to pass to func() + :returns: List of values returned from func() + """ + if not self.extensions: + # FIXME: Use a more specific exception class here. + raise RuntimeError('No %s extensions found' % self.namespace) + response = [] + for e in self.extensions: + self._invoke_one_plugin(response.append, func, e, args, kwds) + return response + + @staticmethod + def _call_extension_method(extension, method_name, *args, **kwds): + return getattr(extension.obj, method_name)(*args, **kwds) + + def map_method(self, method_name, *args, **kwds): + """Iterate over the extensions invoking a method by name. + + This is equivalent of using :meth:`map` with func set to + `lambda x: x.obj.method_name()` + while being more convenient. + + Exceptions raised from within the called method are propagated up + and processing stopped if self.propagate_map_exceptions is True, + otherwise they are logged and ignored. + + .. versionadded:: 0.12 + + :param method_name: The extension method name + to call for each extension. + :param args: Variable arguments to pass to method + :param kwds: Keyword arguments to pass to method + :returns: List of values returned from methods + """ + return self.map(self._call_extension_method, + method_name, *args, **kwds) + + def _invoke_one_plugin(self, response_callback, func, e, args, kwds): + try: + response_callback(func(e, *args, **kwds)) + except Exception as err: + if self.propagate_map_exceptions: + raise + else: + LOG.error('error calling %r: %s', e.name, err) + LOG.exception(err) + + def __iter__(self): + """Produce iterator for the manager. + + Iterating over an ExtensionManager produces the :class:`Extension` + instances in the order they would be invoked. + """ + return iter(self.extensions) + + def __getitem__(self, name): + """Return the named extension. + + Accessing an ExtensionManager as a dictionary (``em['name']``) + produces the :class:`Extension` instance with the + specified name. + """ + if self._extensions_by_name is None: + d = {} + for e in self.extensions: + d[e.name] = e + self._extensions_by_name = d + return self._extensions_by_name[name] diff --git a/libs/stevedore/hook.py b/libs/stevedore/hook.py new file mode 100755 index 00000000..d2570db0 --- /dev/null +++ b/libs/stevedore/hook.py @@ -0,0 +1,64 @@ +from .named import NamedExtensionManager + + +class HookManager(NamedExtensionManager): + """Coordinate execution of multiple extensions using a common name. + + :param namespace: The namespace for the entry points. + :type namespace: str + :param name: The name of the hooks to load. + :type name: str + :param invoke_on_load: Boolean controlling whether to invoke the + object returned by the entry point after the driver is loaded. + :type invoke_on_load: bool + :param invoke_args: Positional arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_args: tuple + :param invoke_kwds: Named arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_kwds: dict + :param on_load_failure_callback: Callback function that will be called when + a entrypoint can not be loaded. The arguments that will be provided + when this is called (when an entrypoint fails to load) are + (manager, entrypoint, exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + """ + + def __init__(self, namespace, name, + invoke_on_load=False, invoke_args=(), invoke_kwds={}, + on_load_failure_callback=None, + verify_requirements=False): + super(HookManager, self).__init__( + namespace, + [name], + invoke_on_load=invoke_on_load, + invoke_args=invoke_args, + invoke_kwds=invoke_kwds, + on_load_failure_callback=on_load_failure_callback, + verify_requirements=verify_requirements, + ) + + def _init_attributes(self, namespace, names, name_order=False, + propagate_map_exceptions=False, + on_load_failure_callback=None): + super(HookManager, self)._init_attributes( + namespace, names, + propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback) + self._name = names[0] + + def __getitem__(self, name): + """Return the named extensions. + + Accessing a HookManager as a dictionary (``em['name']``) + produces a list of the :class:`Extension` instance(s) with the + specified name, in the order they would be invoked by map(). + """ + if name != self._name: + raise KeyError(name) + return self.extensions diff --git a/libs/stevedore/named.py b/libs/stevedore/named.py new file mode 100755 index 00000000..18fe235b --- /dev/null +++ b/libs/stevedore/named.py @@ -0,0 +1,124 @@ +from .extension import ExtensionManager + + +class NamedExtensionManager(ExtensionManager): + """Loads only the named extensions. + + This is useful for explicitly enabling extensions in a + configuration file, for example. + + :param namespace: The namespace for the entry points. + :type namespace: str + :param names: The names of the extensions to load. + :type names: list(str) + :param invoke_on_load: Boolean controlling whether to invoke the + object returned by the entry point after the driver is loaded. + :type invoke_on_load: bool + :param invoke_args: Positional arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_args: tuple + :param invoke_kwds: Named arguments to pass when invoking + the object returned by the entry point. Only used if invoke_on_load + is True. + :type invoke_kwds: dict + :param name_order: If true, sort the loaded extensions to match the + order used in ``names``. + :type name_order: bool + :param propagate_map_exceptions: Boolean controlling whether exceptions + are propagated up through the map call or whether they are logged and + then ignored + :type propagate_map_exceptions: bool + :param on_load_failure_callback: Callback function that will be called when + a entrypoint can not be loaded. The arguments that will be provided + when this is called (when an entrypoint fails to load) are + (manager, entrypoint, exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + + """ + + def __init__(self, namespace, names, + invoke_on_load=False, invoke_args=(), invoke_kwds={}, + name_order=False, propagate_map_exceptions=False, + on_load_failure_callback=None, + verify_requirements=False): + self._init_attributes( + namespace, names, name_order=name_order, + propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback) + extensions = self._load_plugins(invoke_on_load, + invoke_args, + invoke_kwds, + verify_requirements) + self._init_plugins(extensions) + + @classmethod + def make_test_instance(cls, extensions, namespace='TESTING', + propagate_map_exceptions=False, + on_load_failure_callback=None, + verify_requirements=False): + """Construct a test NamedExtensionManager + + Test instances are passed a list of extensions to use rather than + loading them from entry points. + + :param extensions: Pre-configured Extension instances + :type extensions: list of :class:`~stevedore.extension.Extension` + :param namespace: The namespace for the manager; used only for + identification since the extensions are passed in. + :type namespace: str + :param propagate_map_exceptions: Boolean controlling whether exceptions + are propagated up through the map call or whether they are logged + and then ignored + :type propagate_map_exceptions: bool + :param on_load_failure_callback: Callback function that will + be called when a entrypoint can not be loaded. The + arguments that will be provided when this is called (when + an entrypoint fails to load) are (manager, entrypoint, + exception) + :type on_load_failure_callback: function + :param verify_requirements: Use setuptools to enforce the + dependencies of the plugin(s) being loaded. Defaults to False. + :type verify_requirements: bool + :return: The manager instance, initialized for testing + + """ + + o = cls.__new__(cls) + names = [e.name for e in extensions] + o._init_attributes(namespace, names, + propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback) + o._init_plugins(extensions) + return o + + def _init_attributes(self, namespace, names, name_order=False, + propagate_map_exceptions=False, + on_load_failure_callback=None): + super(NamedExtensionManager, self)._init_attributes( + namespace, propagate_map_exceptions=propagate_map_exceptions, + on_load_failure_callback=on_load_failure_callback) + + self._names = names + self._name_order = name_order + + def _init_plugins(self, extensions): + super(NamedExtensionManager, self)._init_plugins(extensions) + + if self._name_order: + self.extensions = [self[n] for n in self._names] + + def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds, + verify_requirements): + # Check the name before going any further to prevent + # undesirable code from being loaded at all if we are not + # going to use it. + if ep.name not in self._names: + return None + return super(NamedExtensionManager, self)._load_one_plugin( + ep, invoke_on_load, invoke_args, invoke_kwds, + verify_requirements, + )