From 03eb2801a3ad38a39e9cf127c05ab710bf38ee1d Mon Sep 17 00:00:00 2001 From: Dolph Mathews Date: Wed, 19 Dec 2012 10:04:21 -0600 Subject: Driver registry Uses automatic dependency injection to provide controllers with driver interfaces (identity_api, token_api, etc). See tests/test_injection.py for a self-contained example. Change-Id: I255087de534292fbf57a45b19f97488f831f607c --- keystone/common/controller.py | 10 ++----- keystone/common/dependency.py | 67 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 keystone/common/dependency.py (limited to 'keystone/common') diff --git a/keystone/common/controller.py b/keystone/common/controller.py index 5f351411..ee7ce5b5 100644 --- a/keystone/common/controller.py +++ b/keystone/common/controller.py @@ -1,6 +1,7 @@ import uuid import functools +from keystone.common import dependency from keystone.common import logging from keystone.common import wsgi from keystone import exception @@ -55,15 +56,10 @@ def protected(f): return wrapper +@dependency.requires('identity_api', 'policy_api', 'token_api') class V2Controller(wsgi.Application): """Base controller class for Identity API v2.""" - - def __init__(self, catalog_api, identity_api, policy_api, token_api): - self.catalog_api = catalog_api - self.identity_api = identity_api - self.policy_api = policy_api - self.token_api = token_api - super(V2Controller, self).__init__() + pass class V3Controller(V2Controller): diff --git a/keystone/common/dependency.py b/keystone/common/dependency.py new file mode 100644 index 00000000..dc3e4ac4 --- /dev/null +++ b/keystone/common/dependency.py @@ -0,0 +1,67 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 OpenStack LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +REGISTRY = {} + + +class UnresolvableDependencyException(Exception): + def __init__(self, name): + msg = 'Unregistered dependency: %s' % name + super(UnresolvableDependencyException, self).__init__(msg) + + +def provider(name): + """Register the wrapped dependency provider under the specified name.""" + def wrapper(cls): + def wrapped(init): + def __wrapped_init__(self, *args, **kwargs): + """Initialize the wrapped object and add it to the registry.""" + init(self, *args, **kwargs) + REGISTRY[name] = self + + return __wrapped_init__ + + cls.__init__ = wrapped(cls.__init__) + return cls + + return wrapper + + +def requires(*dependencies): + """Inject specified dependencies from the registry into the instance.""" + def wrapper(self, *args, **kwargs): + """Inject each dependency from the registry.""" + self.__wrapped_init__(*args, **kwargs) + + for dependency in self._dependencies: + if dependency not in REGISTRY: + raise UnresolvableDependencyException(dependency) + setattr(self, dependency, REGISTRY[dependency]) + + def wrapped(cls): + """Note the required dependencies on the object for later injection. + + The dependencies of the parent class are combined with that of the + child class to create a new set of dependencies. + """ + existing_dependencies = getattr(cls, '_dependencies', set()) + cls._dependencies = existing_dependencies.union(dependencies) + if not hasattr(cls, '__wrapped_init__'): + cls.__wrapped_init__ = cls.__init__ + cls.__init__ = wrapper + return cls + + return wrapped -- cgit