From 83098c78d0b296b5d5212033a1ab41886aac298a Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Fri, 4 Jan 2013 19:14:21 -0500 Subject: [PATCH] Check the names of plugins before importing them Update NamedExtensionManager to check the names of the plugins before loading any code to avoid importing anything we are not going to use. Fixes issue #4 Change-Id: I27b19cb42ca3d165ce45953281b82e394c4539a2 Signed-off-by: Doug Hellmann --- docs/source/history.rst | 3 +++ stevedore/named.py | 18 +++++++++++++----- stevedore/tests/test_named.py | 30 +++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 8 deletions(-) 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 == []