diff options
author | Daniel Rocco <drocco@thebrightlink.com> | 2013-11-23 23:07:07 -0500 |
---|---|---|
committer | Daniel Rocco <drocco@thebrightlink.com> | 2013-11-23 23:12:48 -0500 |
commit | e2d86a8a0901e30db3697e6a71bc191a8cec4cbd (patch) | |
tree | f586c066e7796fca0e82edaf09d3098f73be50dd | |
parent | 9c7558aeef8fcafca4b45d2c5bcf88e5a4e36790 (diff) | |
download | stevedore-e2d86a8a0901e30db3697e6a71bc191a8cec4cbd.tar.gz |
simplify test instance factory contract
After some discussion, the make_test_instance factories expect a final
list of extensions, which simplifies creation and keeps responsibilities
better focused. Users who wish to test their check functions should do
so separately.
-rw-r--r-- | stevedore/dispatch.py | 3 | ||||
-rw-r--r-- | stevedore/driver.py | 15 | ||||
-rw-r--r-- | stevedore/enabled.py | 34 | ||||
-rw-r--r-- | stevedore/extension.py | 24 | ||||
-rw-r--r-- | stevedore/hook.py | 30 | ||||
-rw-r--r-- | stevedore/named.py | 68 | ||||
-rw-r--r-- | stevedore/tests/test_driver.py | 42 | ||||
-rw-r--r-- | stevedore/tests/test_enabled.py | 38 | ||||
-rw-r--r-- | stevedore/tests/test_test_manager.py | 219 |
9 files changed, 248 insertions, 225 deletions
diff --git a/stevedore/dispatch.py b/stevedore/dispatch.py index 6536ee9..2ff455d 100644 --- a/stevedore/dispatch.py +++ b/stevedore/dispatch.py @@ -142,6 +142,9 @@ class NameDispatchExtensionManager(DispatchExtensionManager): invoke_kwds=invoke_kwds, propagate_map_exceptions=propagate_map_exceptions, ) + + 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): diff --git a/stevedore/driver.py b/stevedore/driver.py index d8bcd12..56e408d 100644 --- a/stevedore/driver.py +++ b/stevedore/driver.py @@ -32,7 +32,7 @@ class DriverManager(NamedExtensionManager): ) @classmethod - def make_test_instance(cls, available_extensions, name, + def make_test_instance(cls, extension, namespace='TESTING', propagate_map_exceptions=False): """Construct a test DriverManager @@ -41,12 +41,11 @@ class DriverManager(NamedExtensionManager): the name argument to determine which of the available_extensions should be used as the driver. - :param available_extensions: Pre-configured Extension instances - available for use - :type available_extensions: list of - :class:`~stevedore.extension.Extension` - :param name: The name of the driver to use. - :type name: str + :param extension: Pre-configured Extension instances + :type extension: 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 @@ -56,7 +55,7 @@ class DriverManager(NamedExtensionManager): """ o = super(DriverManager, cls).make_test_instance( - available_extensions, [name], + [extension], namespace=namespace, propagate_map_exceptions=propagate_map_exceptions) return o diff --git a/stevedore/enabled.py b/stevedore/enabled.py index f277216..b6260b1 100644 --- a/stevedore/enabled.py +++ b/stevedore/enabled.py @@ -47,40 +47,6 @@ class EnabledExtensionManager(ExtensionManager): propagate_map_exceptions=propagate_map_exceptions, ) - @classmethod - def make_test_instance(cls, available_extensions, check_func, - propagate_map_exceptions=False): - """Construct a test EnabledExtensionManager - - Test instances are passed a list of extensions to work from rather - than loading them from entry points, filtering the available - extensions to only those extensions that pass a check function. - - :param available_extensions: Pre-configured Extension instances - available for use - :type available_extensions: list of - :class:`~stevedore.extension.Extension` - :param check_func: Function to determine which extensions to load. - :type check_func: callable - :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 - :return: The manager instance, initialized for testing - - """ - - # simulate excluding plugins not passing check_func, which normally - # happens in _load_one_plugin - extensions = [extension for extension in available_extensions - if check_func(extension)] - - o = super(EnabledExtensionManager, cls).make_test_instance( - extensions, propagate_map_exceptions=propagate_map_exceptions) - - o.check_func = check_func - return o - def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds): ext = super(EnabledExtensionManager, self)._load_one_plugin( ep, invoke_on_load, invoke_args, invoke_kwds, diff --git a/stevedore/extension.py b/stevedore/extension.py index ec71279..2e29a95 100644 --- a/stevedore/extension.py +++ b/stevedore/extension.py @@ -73,15 +73,16 @@ class ExtensionManager(object): invoke_args=(), invoke_kwds={}, propagate_map_exceptions=False): - self.namespace = namespace + self._init_attributes( + namespace, propagate_map_exceptions=propagate_map_exceptions) extensions = self._load_plugins(invoke_on_load, invoke_args, invoke_kwds) - self._init_plugins(extensions, - propagate_map_exceptions=propagate_map_exceptions) + self._init_plugins(extensions) @classmethod - def make_test_instance(cls, extensions, propagate_map_exceptions=False): + def make_test_instance(cls, extensions, namespace='TESTING', + propagate_map_exceptions=False): """Construct a test ExtensionManager Test instances are passed a list of extensions to work from rather @@ -89,6 +90,9 @@ class ExtensionManager(object): :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 @@ -98,13 +102,17 @@ class ExtensionManager(object): """ o = cls.__new__(cls) - o._init_plugins(extensions, - propagate_map_exceptions=propagate_map_exceptions) + o._init_attributes(namespace, + propagate_map_exceptions=propagate_map_exceptions) + o._init_plugins(extensions) return o - def _init_plugins(self, extensions, propagate_map_exceptions=False): - self.extensions = extensions + def _init_attributes(self, namespace, propagate_map_exceptions=False): + self.namespace = namespace self.propagate_map_exceptions = propagate_map_exceptions + + def _init_plugins(self, extensions): + self.extensions = extensions self._extensions_by_name = None ENTRY_POINT_CACHE = {} diff --git a/stevedore/hook.py b/stevedore/hook.py index 29f845a..b7cdef8 100644 --- a/stevedore/hook.py +++ b/stevedore/hook.py @@ -23,7 +23,6 @@ class HookManager(NamedExtensionManager): def __init__(self, namespace, name, invoke_on_load=False, invoke_args=(), invoke_kwds={}): - self._name = name super(HookManager, self).__init__( namespace, [name], @@ -32,29 +31,12 @@ class HookManager(NamedExtensionManager): invoke_kwds=invoke_kwds, ) - @classmethod - def make_test_instance(cls, available_extensions, name): - """Construct a test HookManager - - Test instances are passed a list of extensions to work from rather - than loading them from entry points, filtering the available - extensions to only those extensions whose name matches the name - argument. - - :param available_extensions: Pre-configured Extension instances - available for use - :type available_extensions: list of - :class:`~stevedore.extension.Extension` - :param name: The name of the hooks to use. - :type name: str - :return: The manager instance, initialized for testing - - """ - - o = super(HookManager, cls).make_test_instance(available_extensions, - [name]) - o._name = name - return o + def _init_attributes(self, namespace, names, name_order=False, + propagate_map_exceptions=False): + super(HookManager, self)._init_attributes( + namespace, names, + propagate_map_exceptions=propagate_map_exceptions) + self._name = names[0] def __getitem__(self, name): """Return the named extensions. diff --git a/stevedore/named.py b/stevedore/named.py index 0b1ce97..b4f620c 100644 --- a/stevedore/named.py +++ b/stevedore/named.py @@ -35,35 +35,27 @@ class NamedExtensionManager(ExtensionManager): def __init__(self, namespace, names, invoke_on_load=False, invoke_args=(), invoke_kwds={}, name_order=False, propagate_map_exceptions=False): - self._names = names - self._name_order = name_order - super(NamedExtensionManager, self).__init__( - namespace, - invoke_on_load=invoke_on_load, - invoke_args=invoke_args, - invoke_kwds=invoke_kwds, - propagate_map_exceptions=propagate_map_exceptions, - ) + self._init_attributes( + namespace, names, name_order=name_order, + propagate_map_exceptions=propagate_map_exceptions) + extensions = self._load_plugins(invoke_on_load, + invoke_args, + invoke_kwds) + self._init_plugins(extensions) @classmethod - def make_test_instance(cls, available_extensions, names=None, - name_order=False, propagate_map_exceptions=False): + def make_test_instance(cls, extensions, namespace='TESTING', + propagate_map_exceptions=False): """Construct a test NamedExtensionManager - Test instances are passed a list of extensions to work from rather - than loading them from entry points, filtering the available - extensions to only those extensions whose names are listed in the - names argument. - - :param available_extensions: Pre-configured Extension instances - available for use - :type available_extensions: list of - :class:`~stevedore.extension.Extension` - :param names: The names of the extensions to use. - :type names: list(str) - :param name_order: If true, sort the extensions to match the order - used in ``names``. - :type name_order: bool + 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 @@ -73,22 +65,22 @@ class NamedExtensionManager(ExtensionManager): """ o = cls.__new__(cls) - o.namespace = 'TESTING' - o._names = names - o._name_order = name_order + names = [e.name for e in extensions] + o._init_attributes(namespace, names, + propagate_map_exceptions=propagate_map_exceptions) + o._init_plugins(extensions) + return o - # simulate excluding plugins not listed in names, which normally - # happens in _load_one_plugin - extensions = [extension for extension in available_extensions - if extension.name in names] + def _init_attributes(self, namespace, names, name_order=False, + propagate_map_exceptions=False): + super(NamedExtensionManager, self)._init_attributes( + namespace, propagate_map_exceptions=propagate_map_exceptions) - o._init_plugins(extensions, - propagate_map_exceptions=propagate_map_exceptions) - return o + self._names = names + self._name_order = name_order - def _init_plugins(self, extensions, propagate_map_exceptions=False): - super(NamedExtensionManager, self)._init_plugins( - extensions, propagate_map_exceptions=propagate_map_exceptions) + def _init_plugins(self, extensions): + super(NamedExtensionManager, self)._init_plugins(extensions) if self._name_order: self.extensions = [self[n] for n in self._names] diff --git a/stevedore/tests/test_driver.py b/stevedore/tests/test_driver.py index df43110..70ec54c 100644 --- a/stevedore/tests/test_driver.py +++ b/stevedore/tests/test_driver.py @@ -1,21 +1,13 @@ """Tests for stevedore.extension """ -from mock import Mock, sentinel -from nose.tools import raises import mock import pkg_resources from stevedore import driver -from stevedore.extension import Extension from stevedore.tests import test_extension -mock_entry_point = Mock(module_name='test.extension', attrs=['obj']) -a_driver = Extension('test_driver', mock_entry_point, sentinel.driver_plugin, - sentinel.driver_obj) - - def test_detect_plugins(): em = driver.DriverManager('stevedore.test.extension', 't1') names = sorted(em.names()) @@ -67,37 +59,3 @@ def test_multiple_drivers(): except RuntimeError as err: assert "Multiple" in str(err), str(err) fep.assert_called_with('stevedore.test.multiple_drivers') - - -@raises(RuntimeError) -def test_instance_driver_not_found_should_raise(): - extension = Extension('a_driver', None, None, None) - driver.DriverManager.make_test_instance( - [extension], 'this_is_not_the_driver_you_are_looking_for') - - -@raises(RuntimeError) -def test_instance_multiple_drivers_found_should_raise(): - extension = Extension('test_driver', mock_entry_point, None, None) - copycat = Extension('test_driver', mock_entry_point, None, None) - - driver.DriverManager.make_test_instance([extension, copycat], - 'test_driver') - - -def test_instance_should_find_named_driver(): - em = driver.DriverManager.make_test_instance([a_driver], 'test_driver') - assert ['test_driver'] == em.names() - - -def test_instance_call(): - def invoke(ext, *args, **kwds): - return (ext.name, args, kwds) - em = driver.DriverManager.make_test_instance([a_driver], 'test_driver') - result = em(invoke, 'a', b='C') - assert result == ('test_driver', ('a',), {'b': 'C'}) - - -def test_instance_driver(): - em = driver.DriverManager.make_test_instance([a_driver], 'test_driver') - assert sentinel.driver_obj == em.driver diff --git a/stevedore/tests/test_enabled.py b/stevedore/tests/test_enabled.py index 92cb7ce..659e710 100644 --- a/stevedore/tests/test_enabled.py +++ b/stevedore/tests/test_enabled.py @@ -1,8 +1,4 @@ -from mock import Mock - from stevedore import enabled -from stevedore.tests.test_test_manager import (test_extension, test_extension2, - excluded, unwanted) def test_enabled(): @@ -31,37 +27,3 @@ def test_enabled_after_load(): ) assert len(em.extensions) == 1 assert em.names() == ['t2'] - - -def test_enabled_manager_should_check_all_extensions(): - check_func = Mock() - - enabled.EnabledExtensionManager.make_test_instance([excluded, - test_extension, - unwanted, - test_extension2], - check_func) - - check_func.assert_any_call(test_extension) - check_func.assert_any_call(test_extension2) - check_func.assert_any_call(excluded) - check_func.assert_any_call(unwanted) - - -def test_enabled_manager_should_include_only_passing_extensions(): - def underscore_checker(extension): - return '_' in extension.name - - em = enabled.EnabledExtensionManager.make_test_instance([excluded, - test_extension, - unwanted, - test_extension2], - underscore_checker) - - extensions = list(em) - - assert test_extension in extensions - assert test_extension2 in extensions - - assert excluded not in extensions - assert unwanted not in extensions diff --git a/stevedore/tests/test_test_manager.py b/stevedore/tests/test_test_manager.py index 2dd42c0..aa995b2 100644 --- a/stevedore/tests/test_test_manager.py +++ b/stevedore/tests/test_test_manager.py @@ -1,13 +1,40 @@ -from mock import Mock +from mock import Mock, sentinel from nose.tools import raises -from stevedore import (ExtensionManager, NamedExtensionManager, HookManager) +from stevedore import (ExtensionManager, NamedExtensionManager, HookManager, + DriverManager, EnabledExtensionManager) +from stevedore.dispatch import (DispatchExtensionManager, + NameDispatchExtensionManager) from stevedore.extension import Extension test_extension = Extension('test_extension', None, None, None) test_extension2 = Extension('another_one', None, None, None) -excluded = Extension('excluded', None, None, None) -unwanted = Extension('unwanted', None, None, None) + +mock_entry_point = Mock(module_name='test.extension', attrs=['obj']) +a_driver = Extension('test_driver', mock_entry_point, sentinel.driver_plugin, + sentinel.driver_obj) + + +# base ExtensionManager + +def test_instance_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = ExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_instance_should_have_default_namespace(): + em = ExtensionManager.make_test_instance([]) + + assert em.namespace + + +def test_instance_should_use_supplied_namespace(): + namespace = 'testing.1.2.3' + em = ExtensionManager.make_test_instance([], namespace=namespace) + + assert namespace == em.namespace def test_extension_name_should_be_listed(): @@ -76,50 +103,176 @@ def test_manager_should_propagate_exceptions(): em.map(func, 1, 2, a='A', b='B') -def test_named_manager_should_include_named_extensions(): - em = NamedExtensionManager.make_test_instance([excluded, - test_extension, - unwanted, - test_extension2], - ['test_extension', - 'another_one']) +# NamedExtensionManager + +def test_named_manager_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = NamedExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_named_manager_should_have_default_namespace(): + em = NamedExtensionManager.make_test_instance([]) + + assert em.namespace + + +def test_named_manager_should_use_supplied_namespace(): + namespace = 'testing.1.2.3' + em = NamedExtensionManager.make_test_instance([], namespace=namespace) + + assert namespace == em.namespace - extensions = list(em) - assert test_extension in extensions - assert test_extension2 in extensions +def test_named_manager_should_populate_names(): + extensions = [test_extension, test_extension2] + em = NamedExtensionManager.make_test_instance(extensions) + assert ['test_extension', 'another_one'] == em.names() -def test_named_manager_should_not_include_unnamed_extensions(): - em = NamedExtensionManager.make_test_instance([excluded, - test_extension, - unwanted, - test_extension2], - ['test_extension', - 'another_one']) - extensions = list(em) +# HookManager - assert excluded not in extensions - assert unwanted not in extensions +def test_hook_manager_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = HookManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_hook_manager_should_be_first_extension_name(): + extensions = [test_extension, test_extension2] + em = HookManager.make_test_instance(extensions) + + # This will raise KeyError if the names don't match + assert em[test_extension.name] + + +def test_hook_manager_should_have_default_namespace(): + em = HookManager.make_test_instance([test_extension]) + + assert em.namespace + + +def test_hook_manager_should_use_supplied_namespace(): + namespace = 'testing.1.2.3' + em = HookManager.make_test_instance([test_extension], namespace=namespace) + + assert namespace == em.namespace def test_hook_manager_should_return_named_extensions(): hook1 = Extension('captain', None, None, None) hook2 = Extension('captain', None, None, None) - em = HookManager.make_test_instance([hook1, hook2], 'captain') + em = HookManager.make_test_instance([hook1, hook2]) assert [hook1, hook2] == em['captain'] -def test_hook_manager_should_not_include_unnamed_extensions(): - em = HookManager.make_test_instance([excluded, test_extension, - unwanted, test_extension2], - 'test_extension') +# DriverManager + +def test_driver_manager_should_use_supplied_extension(): + em = DriverManager.make_test_instance(a_driver) + + assert [a_driver] == em.extensions + + +def test_driver_manager_should_have_default_namespace(): + em = DriverManager.make_test_instance(a_driver) + + assert em.namespace + + +def test_driver_manager_should_use_supplied_namespace(): + namespace = 'testing.1.2.3' + em = DriverManager.make_test_instance(a_driver, namespace=namespace) + + assert namespace == em.namespace + + +def test_instance_should_use_driver_name(): + em = DriverManager.make_test_instance(a_driver) + + assert ['test_driver'] == em.names() + + +def test_instance_call(): + def invoke(ext, *args, **kwds): + return ext.name, args, kwds + + em = DriverManager.make_test_instance(a_driver) + result = em(invoke, 'a', b='C') + + assert result == ('test_driver', ('a',), {'b': 'C'}) + + +def test_instance_driver_property(): + em = DriverManager.make_test_instance(a_driver) + + assert sentinel.driver_obj == em.driver + + +# EnabledExtensionManager + +def test_enabled_instance_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = EnabledExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +# DispatchExtensionManager + +def test_dispatch_instance_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = DispatchExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_dispatch_map_should_invoke_filter_for_extensions(): + em = DispatchExtensionManager.make_test_instance([test_extension, + test_extension2]) + + filter_func = Mock(return_value=False) + + args = ('A',) + kw = {'big': 'Cheese'} + + em.map(filter_func, None, *args, **kw) + + filter_func.assert_any_call(test_extension, *args, **kw) + filter_func.assert_any_call(test_extension2, *args, **kw) + + +# NameDispatchExtensionManager + +def test_name_dispatch_instance_should_use_supplied_extensions(): + extensions = [test_extension, test_extension2] + em = NameDispatchExtensionManager.make_test_instance(extensions) + + assert extensions == em.extensions + + +def test_name_dispatch_instance_should_build_extension_name_map(): + extensions = [test_extension, test_extension2] + em = NameDispatchExtensionManager.make_test_instance(extensions) + + assert test_extension == em.by_name[test_extension.name] + assert test_extension2 == em.by_name[test_extension2.name] + + +def test_named_dispatch_map_should_invoke_filter_for_extensions(): + em = NameDispatchExtensionManager.make_test_instance([test_extension, + test_extension2]) + + func = Mock() + + args = ('A',) + kw = {'BIGGER': 'Cheese'} - extensions = em['test_extension'] + em.map(['test_extension'], func, *args, **kw) - assert test_extension2 not in extensions - assert excluded not in extensions - assert unwanted not in extensions + func.assert_called_once_with(test_extension, *args, **kw) |