diff options
author | Jenkins <jenkins@review.openstack.org> | 2013-08-02 00:34:02 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2013-08-02 00:34:02 +0000 |
commit | 3448ffb5aa8c4794bdb7057280b72971606ad04e (patch) | |
tree | 662ed4b43a457c0192e82453d6eee75d571d606e | |
parent | 104f04e416d080052e5f8bede8727475898f2741 (diff) | |
parent | 3c6cc9e838cacd1f7c0a3cfc89b0f66b23851803 (diff) | |
download | keystone-3448ffb5aa8c4794bdb7057280b72971606ad04e.tar.gz keystone-3448ffb5aa8c4794bdb7057280b72971606ad04e.tar.xz keystone-3448ffb5aa8c4794bdb7057280b72971606ad04e.zip |
Merge "Handle circular dependencies"
-rw-r--r-- | keystone/common/dependency.py | 33 | ||||
-rw-r--r-- | keystone/service.py | 3 | ||||
-rw-r--r-- | keystone/test.py | 2 | ||||
-rw-r--r-- | tests/test_injection.py | 27 |
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. |