diff options
author | Dolph Mathews <dolph.mathews@gmail.com> | 2014-09-10 14:31:05 +0000 |
---|---|---|
committer | Dolph Mathews <dolph.mathews@gmail.com> | 2014-09-10 14:31:05 +0000 |
commit | 5e432fc52860ce7789f646dcde52c4e9b1792cf2 (patch) | |
tree | 4dd30e1dca31ed6d72140a98cf08e0e7d53f7ffc | |
parent | 8a5913be1f602dc65193233551ff13ada3424e5d (diff) | |
download | keystone-5e432fc52860ce7789f646dcde52c4e9b1792cf2.tar.gz |
improve dependency injection doc strings
This also renames the provider_name argument to better convey it's
status as a kwarg for internal use only.
Change-Id: Ie1afee4e37cfa149ddb73a001985c98aa90b97a5
-rw-r--r-- | keystone/common/dependency.py | 80 |
1 files changed, 45 insertions, 35 deletions
diff --git a/keystone/common/dependency.py b/keystone/common/dependency.py index c29f727dd..d8808c20b 100644 --- a/keystone/common/dependency.py +++ b/keystone/common/dependency.py @@ -14,14 +14,15 @@ """This module provides support for dependency injection. -Providers are registered via the 'provider' decorator, and dependencies on them -are registered with 'requires' or 'optional'. Providers are available to their -consumers via an attribute. See the documentation for the individual functions -for more detail. +Providers are registered via the ``@provider()`` decorator, and dependencies on +them are registered with ``@requires()`` or ``@optional()``. Providers are +available to their consumers via an attribute. See the documentation for the +individual functions for more detail. See also: https://en.wikipedia.org/wiki/Dependency_injection + """ import six @@ -38,8 +39,10 @@ _factories = {} class UnresolvableDependencyException(Exception): - """An UnresolvableDependencyException is raised when a required dependency - is not resolvable; see 'resolve_future_dependencies'. + """Raised when a required dependency is not resolvable. + + See ``resolve_future_dependencies()`` for more details. + """ def __init__(self, name): msg = 'Unregistered dependency: %s' % name @@ -47,11 +50,11 @@ class UnresolvableDependencyException(Exception): def provider(name): - """'provider' is a class decorator used to register providers. + """A class decorator used to register providers. - When 'provider' is used to decorate a class, members of that class will - register themselves as providers for the named dependency. As an example, - In the code fragment:: + When ``@provider()`` is used to decorate a class, members of that class + will register themselves as providers for the named dependency. As an + example, In the code fragment:: @dependency.provider('foo_api') class Foo: @@ -62,10 +65,11 @@ def provider(name): foo = Foo() - The object 'foo' will be registered as a provider for 'foo_api'. No more - than one such instance should be created; additional instances will replace - the previous ones, possibly resulting in different instances being used by - different consumers. + The object ``foo`` will be registered as a provider for ``foo_api``. No + more than one such instance should be created; additional instances will + replace the previous ones, possibly resulting in different instances being + used by different consumers. + """ def wrapper(cls): def wrapped(init): @@ -107,7 +111,7 @@ def provider(name): REGISTRY[name] = self register_event_callbacks(self) - resolve_future_dependencies(name) + resolve_future_dependencies(__provider_name=name) return __wrapped_init__ @@ -136,11 +140,11 @@ def _process_dependencies(obj): def requires(*dependencies): - """'requires' is a class decorator used to inject providers into consumers. + """A class decorator used to inject providers into consumers. The required providers will be made available to instances of the decorated - class via an attribute with the same name as the provider. For example, - in the code fragment:: + class via an attribute with the same name as the provider. For example, in + the code fragment:: @dependency.requires('foo_api', 'bar_api') class FooBarClient: @@ -151,15 +155,17 @@ def requires(*dependencies): client = FooBarClient() - The object 'client' will have attributes named 'foo_api' and 'bar_api', - which are instances of the named providers. + The object ``client`` will have attributes named ``foo_api`` and + ``bar_api``, which are instances of the named providers. Objects must not rely on the existence of these attributes until after - 'resolve_future_dependencies' has been called; they may not exist + ``resolve_future_dependencies()`` has been called; they may not exist beforehand. - Dependencies registered via 'required' must have providers - if not, an - exception will be raised when 'resolve_future_dependencies' is called. + Dependencies registered via ``@required()`` must have providers; if not, + an ``UnresolvableDependencyException`` will be raised when + ``resolve_future_dependencies()`` is called. + """ def wrapper(self, *args, **kwargs): """Inject each dependency from the registry.""" @@ -171,6 +177,7 @@ def requires(*dependencies): 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) @@ -183,8 +190,10 @@ def requires(*dependencies): def optional(*dependencies): - """'optional' is the same as 'requires', except that the dependencies are - optional - if no provider is available, the attributes will be set to None. + """Similar to ``@requires()``, except that the dependencies are optional. + + If no provider is available, the attributes will be set to ``None``. + """ def wrapper(self, *args, **kwargs): """Inject each dependency from the registry.""" @@ -196,8 +205,8 @@ def optional(*dependencies): The dependencies of the parent class are combined with that of the child class to create a new set of dependencies. - """ + """ existing_optionals = getattr(cls, '_optionals', set()) cls._optionals = existing_optionals.union(dependencies) if not hasattr(cls, '__wrapped_init__'): @@ -208,8 +217,8 @@ def optional(*dependencies): return wrapped -def resolve_future_dependencies(provider_name=None): - """'resolve_future_dependencies' forces injection of all dependencies. +def resolve_future_dependencies(__provider_name=None): + """Forces injection of all dependencies. Before this function is called, circular dependencies may not have been injected. This function should be called only once, after all global @@ -217,21 +226,22 @@ def resolve_future_dependencies(provider_name=None): call, it must not have circular dependencies. If any required dependencies are unresolvable, this function will raise an - UnresolvableDependencyException. + ``UnresolvableDependencyException``. Outside of this module, this function should be called with no arguments; - the optional argument is used internally, and should be treated as an - implementation detail. + the optional argument, ``__provider_name`` is used internally, and should + be treated as an implementation detail. + """ new_providers = dict() - if provider_name: + if __provider_name: # A provider was registered, so take care of any objects depending on # it. - targets = _future_dependencies.pop(provider_name, []) - targets.extend(_future_optionals.pop(provider_name, [])) + targets = _future_dependencies.pop(__provider_name, []) + targets.extend(_future_optionals.pop(__provider_name, [])) for target in targets: - setattr(target, provider_name, REGISTRY[provider_name]) + setattr(target, __provider_name, REGISTRY[__provider_name]) return |