From e649cea843b8453d268da8ea74ea180f513d5ea3 Mon Sep 17 00:00:00 2001 From: Doug Hellmann <doug@doughellmann.com> Date: Wed, 27 May 2015 18:08:02 +0000 Subject: [PATCH] Do not check requirements when loading entry points Update the calls to pkg_resources to avoid forcing a requirements check when the plugins are being loaded. There are 2 versions of the entry point API in different releases of setuptools. In one version, the require keyword argument can be passed to load(). In the other, separate methods resolve() and require() need to be used. This change updates the mock and fake objects to support either, since the fakes are subclasses of the EntryPoint class in pkg_resources. It would be better to replace the calls to pkg_resources with stevedore, which provides a more stable API, abstracts away this difference, and provides an API for creating test managers directly. That change would have required more extensive updates to the test suite, though, and since I'm not as familiar with this code base as others will be, I will leave those changes for someone else. Change-Id: I2a9aeb53ccad04c7fa687f25340306b84218f9ff Partial-bug: #1457100 --- novaclient/auth_plugin.py | 9 +++- novaclient/tests/unit/test_auth_plugins.py | 62 ++++++++++++++++------ novaclient/utils.py | 9 +++- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/novaclient/auth_plugin.py b/novaclient/auth_plugin.py index da2c07b26..d729c4d5a 100644 --- a/novaclient/auth_plugin.py +++ b/novaclient/auth_plugin.py @@ -37,7 +37,14 @@ def discover_auth_systems(): ep_name = 'openstack.client.auth_plugin' for ep in pkg_resources.iter_entry_points(ep_name): try: - auth_plugin = ep.load() + # FIXME(dhellmann): It would be better to use stevedore + # here, since it abstracts this difference in behavior + # between versions of setuptools, but this seemed like a + # more expedient fix. + if hasattr(ep, 'resolve') and hasattr(ep, 'require'): + auth_plugin = ep.resolve() + else: + auth_plugin = ep.load(require=False) except (ImportError, pkg_resources.UnknownExtra, AttributeError) as e: logger.debug("ERROR: Cannot load auth plugin %s" % ep.name) logger.debug(e, exc_info=1) diff --git a/novaclient/tests/unit/test_auth_plugins.py b/novaclient/tests/unit/test_auth_plugins.py index 675102ca3..9dc02b02a 100644 --- a/novaclient/tests/unit/test_auth_plugins.py +++ b/novaclient/tests/unit/test_auth_plugins.py @@ -58,7 +58,10 @@ def requested_headers(cs): class DeprecatedAuthPluginTest(utils.TestCase): def test_auth_system_success(self): class MockEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return self.authenticate + + def resolve(self): return self.authenticate def authenticate(self, cls, auth_url): @@ -117,14 +120,20 @@ class DeprecatedAuthPluginTest(utils.TestCase): def test_auth_system_defining_auth_url(self): class MockAuthUrlEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return self.auth_url + + def resolve(self): return self.auth_url def auth_url(self): return "http://faked/v2.0" class MockAuthenticateEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return self.authenticate + + def resolve(self): return self.authenticate def authenticate(self, cls, auth_url): @@ -160,7 +169,10 @@ class DeprecatedAuthPluginTest(utils.TestCase): @mock.patch.object(pkg_resources, "iter_entry_points") def test_client_raises_exc_without_auth_url(self, mock_iter_entry_points): class MockAuthUrlEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return self.auth_url + + def resolve(self): return self.auth_url def auth_url(self): @@ -184,14 +196,17 @@ class AuthPluginTest(utils.TestCase): def test_auth_system_success(self, mock_iter_entry_points, mock_request): """Test that we can authenticate using the auth system.""" class MockEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return FakePlugin + + def resolve(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): def authenticate(self, cls, auth_url): cls._authenticate(auth_url, {"fake": "me"}) - mock_iter_entry_points.side_effect = lambda _t: [ + mock_iter_entry_points.side_effect = lambda _t, name=None: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] mock_request.side_effect = mock_http_request() @@ -227,10 +242,13 @@ class AuthPluginTest(utils.TestCase): return parser class MockEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): return FakePlugin - mock_iter_entry_points.side_effect = lambda _t: [ + def resolve(self): + return FakePlugin + + mock_iter_entry_points.side_effect = lambda _t, name=None: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] parser = argparse.ArgumentParser() @@ -244,7 +262,10 @@ class AuthPluginTest(utils.TestCase): def test_parse_auth_system_options(self, mock_iter_entry_points): """Test that we can parse the auth system options.""" class MockEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return FakePlugin + + def resolve(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): @@ -254,7 +275,7 @@ class AuthPluginTest(utils.TestCase): def parse_opts(self, args): return self.opts - mock_iter_entry_points.side_effect = lambda _t: [ + mock_iter_entry_points.side_effect = lambda _t, name=None: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] auth_plugin.discover_auth_systems() @@ -267,14 +288,17 @@ class AuthPluginTest(utils.TestCase): def test_auth_system_defining_url(self, mock_iter_entry_points): """Test the auth_system defining an url.""" class MockEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return FakePlugin + + def resolve(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): def get_auth_url(self): return "http://faked/v2.0" - mock_iter_entry_points.side_effect = lambda _t: [ + mock_iter_entry_points.side_effect = lambda _t, name=None: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] auth_plugin.discover_auth_systems() @@ -289,13 +313,16 @@ class AuthPluginTest(utils.TestCase): def test_exception_if_no_authenticate(self, mock_iter_entry_points): """Test that no authenticate raises a proper exception.""" class MockEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return FakePlugin + + def resolve(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): pass - mock_iter_entry_points.side_effect = lambda _t: [ + mock_iter_entry_points.side_effect = lambda _t, name=None: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] auth_plugin.discover_auth_systems() @@ -310,13 +337,16 @@ class AuthPluginTest(utils.TestCase): def test_exception_if_no_url(self, mock_iter_entry_points): """Test that no auth_url at all raises exception.""" class MockEntrypoint(pkg_resources.EntryPoint): - def load(self): + def load(self, require=False): + return FakePlugin + + def resolve(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): pass - mock_iter_entry_points.side_effect = lambda _t: [ + mock_iter_entry_points.side_effect = lambda _t, name=None: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] auth_plugin.discover_auth_systems() diff --git a/novaclient/utils.py b/novaclient/utils.py index 50310bfdb..1f4df7a57 100644 --- a/novaclient/utils.py +++ b/novaclient/utils.py @@ -319,7 +319,14 @@ def _load_entry_point(ep_name, name=None): """Try to load the entry point ep_name that matches name.""" for ep in pkg_resources.iter_entry_points(ep_name, name=name): try: - return ep.load() + # FIXME(dhellmann): It would be better to use stevedore + # here, since it abstracts this difference in behavior + # between versions of setuptools, but this seemed like a + # more expedient fix. + if hasattr(ep, 'resolve') and hasattr(ep, 'require'): + return ep.resolve() + else: + return ep.load(require=False) except (ImportError, pkg_resources.UnknownExtra, AttributeError): continue