diff --git a/keystoneauth1/loading/base.py b/keystoneauth1/loading/base.py index 31277d2c..8c2b5f46 100644 --- a/keystoneauth1/loading/base.py +++ b/keystoneauth1/loading/base.py @@ -102,10 +102,28 @@ def get_plugin_options(name): @six.add_metaclass(abc.ABCMeta) class BaseLoader(object): - @abc.abstractproperty + @property def plugin_class(self): raise NotImplemented() + def create_plugin(self, **kwargs): + """Create a plugin from the options available for the loader. + + Given the options that were specified by the loader create an + appropriate plugin. You can override this function in your loader. + + This used to be specified by providing the plugin_class property and + this is still supported, however specifying a property didn't let you + choose a plugin type based upon the options that were presented. + + Override this function if you wish to return different plugins based on + the options presented, otherwise you can simply provide the + plugin_class property. + + Added 2.9 + """ + return self.plugin_class(**kwargs) + @abc.abstractmethod def get_options(self): """Return the list of parameters associated with the auth plugin. @@ -143,7 +161,7 @@ class BaseLoader(object): if missing_required: raise exceptions.MissingRequiredOptions(missing_required) - return self.plugin_class(**kwargs) + return self.create_plugin(**kwargs) def load_from_options_getter(self, getter, **kwargs): """Load a plugin from getter function that returns appropriate values. diff --git a/keystoneauth1/tests/unit/loading/test_loading.py b/keystoneauth1/tests/unit/loading/test_loading.py index 05ce2ac7..f12ccab3 100644 --- a/keystoneauth1/tests/unit/loading/test_loading.py +++ b/keystoneauth1/tests/unit/loading/test_loading.py @@ -19,6 +19,34 @@ from keystoneauth1 import loading from keystoneauth1.tests.unit.loading import utils +class PluginA(object): + + def __init__(self, a): + self.val = a + + +class PluginB(object): + + def __init__(self, b): + self.val = b + + +class TestSplitLoader(loading.BaseLoader): + + def get_options(self): + opts = super(TestSplitLoader, self).get_options() + opts += [loading.Opt('a'), loading.Opt('b')] + return opts + + def create_plugin(self, a=None, b=None, **kwargs): + if a: + return PluginA(a) + if b: + return PluginB(b) + + raise AssertionError('Expected A or B') + + class LoadingTests(utils.TestCase): def test_required_values(self): @@ -100,3 +128,18 @@ class LoadingTests(utils.TestCase): self.assertEqual(99.99, p['a_float']) self.assertEqual('another', p['a_str']) self.assertEqual(66, p['a_int']) + + def test_create_plugin_loader(self): + val_a = uuid.uuid4().hex + val_b = uuid.uuid4().hex + + loader = TestSplitLoader() + + plugin_a = loader.load_from_options(a=val_a) + plugin_b = loader.load_from_options(b=val_b) + + self.assertIsInstance(plugin_a, PluginA) + self.assertIsInstance(plugin_b, PluginB) + + self.assertEqual(val_a, plugin_a.val) + self.assertEqual(val_b, plugin_b.val)