Skip to content

Commit

Permalink
add dispatch managers
Browse files Browse the repository at this point in the history
Signed-off-by: Doug Hellmann <doug.hellmann@dreamhost.com>
  • Loading branch information
Doug Hellmann committed Aug 15, 2012
1 parent 33fe817 commit 4883901
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 1 deletion.
16 changes: 16 additions & 0 deletions docs/source/managers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
================

Expand Down
123 changes: 123 additions & 0 deletions stevedore/dispatch.py
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion stevedore/enabled.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
52 changes: 52 additions & 0 deletions stevedore/tests/test_dispatch.py
Original file line number Diff line number Diff line change
@@ -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.