Add map_method function to managers

This allows to use map() directly over some extension method in a more
convenient way. It's a pattern we often need, so let's built it directly
in Stevedore.
This commit is contained in:
Julien Danjou 2013-09-17 13:52:03 +02:00
parent 7ab52e5475
commit 1482cf264f
4 changed files with 113 additions and 3 deletions

View File

@ -73,6 +73,29 @@ class DispatchExtensionManager(EnabledExtensionManager):
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.
@ -151,3 +174,26 @@ class NameDispatchExtensionManager(DispatchExtensionManager):
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)

View File

@ -140,6 +140,30 @@ class ExtensionManager(object):
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 each one's object method called `method_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))

View File

@ -1,11 +1,12 @@
from stevedore import dispatch
def test_dispatch():
def check_dispatch(ep, *args, **kwds):
def check_dispatch(ep, *args, **kwds):
return ep.name == 't2'
def test_dispatch():
def invoke(ep, *args, **kwds):
return (ep.name, args, kwds)
@ -28,6 +29,20 @@ def test_dispatch():
assert results == expected
def test_dispatch_map_method():
em = dispatch.DispatchExtensionManager(
'stevedore.test.extension',
lambda *args, **kwds: True,
invoke_on_load=True,
invoke_args=('a',),
invoke_kwds={'b': 'B'},
)
results = em.map_method(check_dispatch, 'get_args_and_data',
'first')
assert results == [(('a',), {'b': 'B'}, 'first')]
def test_name_dispatch():
def invoke(ep, *args, **kwds):
@ -72,3 +87,16 @@ def test_name_dispatch_ignore_missing():
)
expected = [('t1', ('first',), {'named': 'named value'})]
assert results == expected
def test_name_dispatch_map_method():
em = dispatch.NameDispatchExtensionManager(
'stevedore.test.extension',
lambda *args, **kwds: True,
invoke_on_load=True,
invoke_args=('a',),
invoke_kwds={'b': 'B'},
)
results = em.map_method(['t3', 't1'], 'get_args_and_data',
'first')
assert results == [(('a',), {'b': 'B'}, 'first')]

View File

@ -11,6 +11,9 @@ class FauxExtension(object):
self.args = args
self.kwds = kwds
def get_args_and_data(self, data):
return self.args, self.kwds, data
def test_detect_plugins():
em = extension.ExtensionManager('stevedore.test.extension')
@ -155,3 +158,12 @@ def test_map_errors_when_no_plugins():
em.map(mapped, 1, 2, a='A', b='B')
except RuntimeError as err:
assert 'No stevedore.test.extension.none extensions found' == str(err)
def test_map_method():
em = extension.ExtensionManager('stevedore.test.extension',
invoke_on_load=True,
)
result = em.map_method('get_args_and_data', 42)
assert set(r[2] for r in result) == set([42])