summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-08-02 00:34:02 +0000
committerGerrit Code Review <review@openstack.org>2013-08-02 00:34:02 +0000
commit3448ffb5aa8c4794bdb7057280b72971606ad04e (patch)
tree662ed4b43a457c0192e82453d6eee75d571d606e
parent104f04e416d080052e5f8bede8727475898f2741 (diff)
parent3c6cc9e838cacd1f7c0a3cfc89b0f66b23851803 (diff)
downloadkeystone-3448ffb5aa8c4794bdb7057280b72971606ad04e.tar.gz
keystone-3448ffb5aa8c4794bdb7057280b72971606ad04e.tar.xz
keystone-3448ffb5aa8c4794bdb7057280b72971606ad04e.zip
Merge "Handle circular dependencies"
-rw-r--r--keystone/common/dependency.py33
-rw-r--r--keystone/service.py3
-rw-r--r--keystone/test.py2
-rw-r--r--tests/test_injection.py27
4 files changed, 64 insertions, 1 deletions
diff --git a/keystone/common/dependency.py b/keystone/common/dependency.py
index 3ed261cc..a640031d 100644
--- a/keystone/common/dependency.py
+++ b/keystone/common/dependency.py
@@ -16,6 +16,8 @@
REGISTRY = {}
+_future_dependencies = {}
+
class UnresolvableDependencyException(Exception):
def __init__(self, name):
@@ -32,6 +34,8 @@ def provider(name):
init(self, *args, **kwargs)
REGISTRY[name] = self
+ resolve_future_dependencies(name)
+
return __wrapped_init__
cls.__init__ = wrapped(cls.__init__)
@@ -48,7 +52,13 @@ def requires(*dependencies):
for dependency in self._dependencies:
if dependency not in REGISTRY:
- raise UnresolvableDependencyException(dependency)
+ if dependency in _future_dependencies:
+ _future_dependencies[dependency] += [self]
+ else:
+ _future_dependencies[dependency] = [self]
+
+ continue
+
setattr(self, dependency, REGISTRY[dependency])
def wrapped(cls):
@@ -67,6 +77,26 @@ def requires(*dependencies):
return wrapped
+def resolve_future_dependencies(provider_name=None):
+ if provider_name:
+ targets = _future_dependencies.pop(provider_name, [])
+
+ for target in targets:
+ setattr(target, provider_name, REGISTRY[provider_name])
+
+ return
+
+ try:
+ for dependency, targets in _future_dependencies.iteritems():
+ if dependency not in REGISTRY:
+ raise UnresolvableDependencyException(dependency)
+
+ for target in targets:
+ setattr(target, dependency, REGISTRY[dependency])
+ finally:
+ _future_dependencies.clear()
+
+
def reset():
"""Reset the registry of providers.
@@ -75,3 +105,4 @@ def reset():
"""
REGISTRY.clear()
+ _future_dependencies.clear()
diff --git a/keystone/service.py b/keystone/service.py
index 6b0c3708..775dfe5d 100644
--- a/keystone/service.py
+++ b/keystone/service.py
@@ -18,6 +18,7 @@ import routes
from keystone import auth
from keystone import catalog
+from keystone.common import dependency
from keystone.common import logging
from keystone.common import wsgi
from keystone import config
@@ -44,6 +45,8 @@ DRIVERS = dict(
trust_api=trust.Manager(),
token_provider_api=token.provider.Manager())
+dependency.resolve_future_dependencies()
+
@logging.fail_gracefully
def public_app_factory(global_conf, **local_conf):
diff --git a/keystone/test.py b/keystone/test.py
index d06ea4c5..55aca3c6 100644
--- a/keystone/test.py
+++ b/keystone/test.py
@@ -271,6 +271,8 @@ class TestCase(NoModule, unittest.TestCase):
manager_name = '%s_api' % manager.__name__.split('.')[-1]
setattr(self, manager_name, manager.Manager())
+ dependency.resolve_future_dependencies()
+
def load_fixtures(self, fixtures):
"""Hacky basic and naive fixture loading based on a python module.
diff --git a/tests/test_injection.py b/tests/test_injection.py
index 08ccd7c7..36cd0126 100644
--- a/tests/test_injection.py
+++ b/tests/test_injection.py
@@ -21,6 +21,10 @@ from keystone.common import dependency
class TestDependencyInjection(unittest.TestCase):
+ def tearDown(self):
+ dependency.reset()
+ super(TestDependencyInjection, self).tearDown()
+
def test_dependency_injection(self):
class Interface(object):
def do_work(self):
@@ -165,6 +169,29 @@ class TestDependencyInjection(unittest.TestCase):
with self.assertRaises(dependency.UnresolvableDependencyException):
Consumer()
+ dependency.resolve_future_dependencies()
+
+ def test_circular_dependency(self):
+ p1_name = uuid.uuid4().hex
+ p2_name = uuid.uuid4().hex
+
+ @dependency.provider(p1_name)
+ @dependency.requires(p2_name)
+ class P1(object):
+ pass
+
+ @dependency.provider(p2_name)
+ @dependency.requires(p1_name)
+ class P2(object):
+ pass
+
+ p1 = P1()
+ p2 = P2()
+
+ dependency.resolve_future_dependencies()
+
+ self.assertIs(getattr(p1, p2_name), p2)
+ self.assertIs(getattr(p2, p1_name), p1)
def test_reset(self):
# Can reset the registry of providers.