diff --git a/docs/source/history.rst b/docs/source/history.rst index a557d72..692c4f0 100644 --- a/docs/source/history.rst +++ b/docs/source/history.rst @@ -6,6 +6,9 @@ dev - Ignore AssertionError exceptions generated when plugins are loaded. + - Update :class:`~stevedore.named.NamedExtensionManager` to check + the name of a plugin before loading its code to avoid importing + anything we are not going to use. 0.7.2 diff --git a/stevedore/named.py b/stevedore/named.py index 17e8b6f..5a0d239 100644 --- a/stevedore/named.py +++ b/stevedore/named.py @@ -1,7 +1,7 @@ -from .enabled import EnabledExtensionManager +from .extension import ExtensionManager -class NamedExtensionManager(EnabledExtensionManager): +class NamedExtensionManager(ExtensionManager): """Loads only the named extensions. This is useful for explictly enabling extensions in a @@ -26,12 +26,20 @@ class NamedExtensionManager(EnabledExtensionManager): def __init__(self, namespace, names, invoke_on_load=False, invoke_args=(), invoke_kwds={}): - def check(ep): - return ep.name in names + self._names = names super(NamedExtensionManager, self).__init__( namespace, - check, invoke_on_load=invoke_on_load, invoke_args=invoke_args, invoke_kwds=invoke_kwds, ) + + def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds): + # 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, + ) diff --git a/stevedore/tests/test_named.py b/stevedore/tests/test_named.py index c61aad2..17af805 100644 --- a/stevedore/tests/test_named.py +++ b/stevedore/tests/test_named.py @@ -1,13 +1,37 @@ from stevedore import named +import mock + def test_named(): em = named.NamedExtensionManager( 'stevedore.test.extension', - ['t1'], + names=['t1'], invoke_on_load=True, invoke_args=('a',), invoke_kwds={'b': 'B'}, ) - assert len(em.extensions) == 1 - assert em.names() == ['t1'] + actual = em.names() + assert actual == ['t1'] + + +def test_enabled_before_load(): + # Set up the constructor for the FauxExtension to cause an + # AssertionError so the test fails if the class is instantiated, + # which should only happen if it is loaded before the name of the + # extension is compared against the names that should be loaded by + # the manager. + init_name = 'stevedore.tests.test_extension.FauxExtension.__init__' + with mock.patch(init_name) as m: + m.side_effect = AssertionError + em = named.NamedExtensionManager( + 'stevedore.test.extension', + # Look for an extension that does not exist so the + # __init__ we mocked should never be invoked. + names=['no-such-extension'], + invoke_on_load=True, + invoke_args=('a',), + invoke_kwds={'b': 'B'}, + ) + actual = em.names() + assert actual == []