Skip to content

Commit 926330d

Browse files
committed
Propagate AttributeErrors when lazily loading plugins
Previously, if an AttributeError was raised in a plugin's make_client method, the plugin simply wouldn't be an attribute of the ClientManager, producing tracebacks like Traceback (most recent call last): File ".../openstackclient/shell.py", line 118, in run ret_val = super(OpenStackShell, self).run(argv) ... File ".../openstackclient/object/v1/container.py", line 150, in take_action data = self.app.client_manager.object_store.container_list( File ".../openstackclient/common/clientmanager.py", line 66, in __getattr__ raise AttributeError(name) AttributeError: object_store This made writing minimal third-party auth plugins difficult, as it obliterated the original AttributeError. Now, AttributeErrors that are raised during plugin initialization will be re-raised as PluginAttributeErrors, and the original traceback will be preserved. This gives much more useful information to plugin developers, as in Traceback (most recent call last): File ".../openstackclient/shell.py", line 118, in run ret_val = super(OpenStackShell, self).run(argv) ... File ".../openstackclient/object/v1/container.py", line 150, in take_action data = self.app.client_manager.object_store.container_list( File ".../openstackclient/common/clientmanager.py", line 57, in __get__ err_val, err_tb) File ".../openstackclient/common/clientmanager.py", line 51, in __get__ self._handle = self.factory(instance) File ".../openstackclient/object/client.py", line 35, in make_client interface=instance._interface, File ".../openstackclient/common/clientmanager.py", line 258, in get_endpoint_for_service_type endpoint = self.auth_ref.service_catalog.url_for( PluginAttributeError: 'NoneType' object has no attribute 'url_for' Change-Id: I0eee7eba6eccc6d471a699a381185c4e76da10bd
1 parent ef68f23 commit 926330d

3 files changed

Lines changed: 24 additions & 1 deletion

File tree

openstackclient/common/clientmanager.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222

2323
from oslo_utils import strutils
2424
import requests
25+
import six
2526

2627
from openstackclient.api import auth
28+
from openstackclient.common import exceptions
2729
from openstackclient.common import session as osc_session
2830
from openstackclient.identity import client as identity_client
2931

@@ -45,7 +47,13 @@ def __init__(self, factory):
4547
def __get__(self, instance, owner):
4648
# Tell the ClientManager to login to keystone
4749
if self._handle is None:
48-
self._handle = self.factory(instance)
50+
try:
51+
self._handle = self.factory(instance)
52+
except AttributeError as err:
53+
# Make sure the failure propagates. Otherwise, the plugin just
54+
# quietly isn't there.
55+
new_err = exceptions.PluginAttributeError(err)
56+
six.reraise(new_err.__class__, new_err, sys.exc_info()[2])
4957
return self._handle
5058

5159

openstackclient/common/exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ class AuthorizationFailure(Exception):
2424
pass
2525

2626

27+
class PluginAttributeError(Exception):
28+
"""A plugin threw an AttributeError while being lazily loaded."""
29+
# This *must not* inherit from AttributeError;
30+
# that would defeat the whole purpose.
31+
pass
32+
33+
2734
class NoTokenLookupException(Exception):
2835
"""This does not support looking up endpoints from an existing token."""
2936
pass

openstackclient/tests/common/test_clientmanager.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
class Container(object):
4343
attr = clientmanager.ClientCache(lambda x: object())
44+
buggy_attr = clientmanager.ClientCache(lambda x: x.foo)
4445

4546
def __init__(self):
4647
pass
@@ -72,6 +73,13 @@ def test_singleton(self):
7273
c = Container()
7374
self.assertEqual(c.attr, c.attr)
7475

76+
def test_attribute_error_propagates(self):
77+
c = Container()
78+
err = self.assertRaises(exc.PluginAttributeError,
79+
getattr, c, 'buggy_attr')
80+
self.assertNotIsInstance(err, AttributeError)
81+
self.assertEqual("'Container' object has no attribute 'foo'", str(err))
82+
7583

7684
class TestClientManager(utils.TestCase):
7785

0 commit comments

Comments
 (0)