Permalink
Browse files

add dispatch managers

Signed-off-by: Doug Hellmann <doug.hellmann@dreamhost.com>
  • Loading branch information...
1 parent 33fe817 commit 4883901c532a33b9fa9f44c38701596f39e50ed8 @dhellmann dhellmann committed Aug 15, 2012
Showing with 192 additions and 1 deletion.
  1. +16 −0 docs/source/managers.rst
  2. +123 −0 stevedore/dispatch.py
  3. +1 −1 stevedore/enabled.py
  4. +52 −0 stevedore/tests/test_dispatch.py
View
@@ -32,6 +32,22 @@ EnabledExtensionManager
:inherited-members:
:show-inheritance:
+DispatchExtensionManager
+========================
+
+.. autoclass:: stevedore.dispatch.DispatchExtensionManager
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+NameDispatchExtensionManager
+============================
+
+.. autoclass:: stevedore.dispatch.NameDispatchExtensionManager
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
ExtensionManager
================
View
@@ -0,0 +1,123 @@
+from .enabled import EnabledExtensionManager
+
+
+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
+ """
+
+ 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 filter_func() and func() 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:
+ try:
+ if filter_func(e, *args, **kwds):
+ response.append(func(e, *args, **kwds))
+ except Exception as err:
+ # FIXME: Provide an argument to control
+ # whether to ignore exceptions in each
+ # plugin or stop processing.
+ LOG.error('error calling %r: %s', e.name, err)
+ LOG.exception(err)
+ return response
+
+
+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.
+
+ :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
+ """
+
+ 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 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()
+ """
+ def name_filter(ext, *args, **kwds):
+ return ext.name in names
+ return super(NameDispatchExtensionManager, self).map(
+ name_filter,
+ func,
+ *args,
+ **kwds)
View
@@ -2,7 +2,7 @@
class EnabledExtensionManager(ExtensionManager):
- """An ExtensionManager that only loads plugins that pass a check function.
+ """Loads only plugins that pass a check function.
The check_func should return a boolean, with ``True`` indicating
that the extension should be loaded and made available and
@@ -0,0 +1,52 @@
+from stevedore import dispatch
+
+
+def test_dispatch():
+
+ def check_dispatch(ep, *args, **kwds):
+ return ep.name == 't2'
+
+ def invoke(ep, *args, **kwds):
+ return (ep.name, args, kwds)
+
+ em = dispatch.DispatchExtensionManager(
+ 'stevedore.test.extension',
+ lambda *args, **kwds: True,
+ invoke_on_load=True,
+ invoke_args=('a',),
+ invoke_kwds={'b': 'B'},
+ )
+ assert len(em.extensions) == 2
+ assert set(em.names()) == set(['t1', 't2'])
+
+ results = em.map(check_dispatch,
+ invoke,
+ 'first',
+ named='named value',
+ )
+ expected = [('t2', ('first',), {'named': 'named value'})]
+ assert results == expected
+
+
+def test_name_dispatch():
+
+ def invoke(ep, *args, **kwds):
+ return (ep.name, args, kwds)
+
+ em = dispatch.NameDispatchExtensionManager(
+ 'stevedore.test.extension',
+ lambda *args, **kwds: True,
+ invoke_on_load=True,
+ invoke_args=('a',),
+ invoke_kwds={'b': 'B'},
+ )
+ assert len(em.extensions) == 2
+ assert set(em.names()) == set(['t1', 't2'])
+
+ results = em.map(['t2'],
+ invoke,
+ 'first',
+ named='named value',
+ )
+ expected = [('t2', ('first',), {'named': 'named value'})]
+ assert results == expected

0 comments on commit 4883901

Please sign in to comment.