keystone/common/dependency.py

1.文件详解

三个全局变量:

_REGISTRY = {} 保存所有已经注册的provider name和provider的对应关系。注册是由装饰器@provider(name)完成。provider其实就是某个类的实例。provider name和provider 类名不一定一样。

其中_set_provider函数就是保存一对provider name和provider到_REGISTRY 中,如果存的时候,发现该provider name已经存在,说明已经有其他实例提供了同名的provider,报错。

_get_provider函数就是获取某个provider name的provider,也就是从_REGISTRY 查。其中参数optional如果是GET_REQUIRED,如果查不到会raise KeyError。optional是GET_OPTIONAL,如果查不到会返回None


_future_dependencies = {}

其中key为provider name,值为依赖这个provider的objs,是一个列表。由@requires装饰器存入。


_factories = {}

感觉没啥用~


@requires(*dependencies),这是一个类装饰器,它的处理流程为:

参数是被修饰的类名,这个类有一个_dependencies的内部变量(没有就创建,初始值为[]),会将*dependencies追加到这个_dependencies的变量中,然后替换这个类的__init__函数为wrapper函数,wrapper函数第一步仍然是执行原来的__init__函数,第二步是执行_process_dependencies(self)方法。

_process_dependencies方法的参数是obj,也就是这个类的唯一实例,首先找obj的_dependencies变量(就是上面说的类的内部变量),对在这个dependencies列表中的每个dependency,查询_REGISTRY,如果已经存在,就调用_get_provider(dependency)拿到provider,然后为obj设置属性self.dependency=get_provider(dependency)。这样这个obj就具有了dependency属性,可以随时使用。

如果dependency在_REGISTRY 中不存在,就将这个obj追加到_future_dependencies 的dependency键值里。


@provider(name),这是一个类装饰器,它的处理流程为:

参数为被修饰的类名,封装这个类的__init__函数为wrapper函数。

wrapper函数:第一步仍然是执行原来的__init__函数,第二步是调用_set_provider函数,将{name: self}放入到_REGISTRY中。第三步是调用resolve_future_dependencies()函数


resolve_future_dependencies(__provider_name=None),

如果__provider_name不为空(本文件内部使用),_future_dependencies 中查询依赖__provider_name的所有obj,再从_REGISTRY 中查询__provider_name的provider。然后为所有obj附上属性:obj.__provider_name = provider

如果__provider_name为空(提供给外部的接口),_future_dependencies 读取所有(dependency, objs)对,再从_REGISTRY 中查询dependency的provider,然后为所有obj附上属性:obj.dependencyprovider


2.源代码

import traceback

from keystone.i18n import _


_REGISTRY = {}

_future_dependencies = {}
_factories = {}


def _set_provider(name, provider):
    _original_provider, where_registered = _REGISTRY.get(name, (None, None))
    if where_registered:
        raise Exception('%s already has a registered provider, at\n%s' %
                        (name, ''.join(where_registered)))
    _REGISTRY[name] = (provider, traceback.format_stack())


GET_REQUIRED = object()
GET_OPTIONAL = object()


def get_provider(name, optional=GET_REQUIRED):
    if optional is GET_REQUIRED:
        return _REGISTRY[name][0]
    return _REGISTRY.get(name, (None, None))[0]


class UnresolvableDependencyException(Exception):
    """Raised when a required dependency is not resolvable.

    See ``resolve_future_dependencies()`` for more details.

    """
    def __init__(self, name, targets):
        msg = _('Unregistered dependency: %(name)s for %(targets)s') % {
            'name': name, 'targets': targets}
        super(UnresolvableDependencyException, self).__init__(msg)


def provider(name):
    """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::

        @dependency.provider('foo_api')
        class Foo:
            def __init__(self):
                ...

            ...

        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.

    """
    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)
                _set_provider(name, self)
                resolve_future_dependencies(__provider_name=name)

            return __wrapped_init__

        cls.__init__ = wrapped(cls.__init__)
        _factories[name] = cls
        return cls
    return wrapper


def _process_dependencies(obj):
    # Any dependencies that can be resolved immediately are resolved.
    # Dependencies that cannot be resolved immediately are stored for
    # resolution in resolve_future_dependencies.

    def process(obj, attr_name, unresolved_in_out):
        for dependency in getattr(obj, attr_name, []):
            if dependency not in _REGISTRY:
                # We don't know about this dependency, so save it for later.
                unresolved_in_out.setdefault(dependency, []).append(obj)
                continue

            setattr(obj, dependency, get_provider(dependency))

    process(obj, '_dependencies', _future_dependencies)


def requires(*dependencies):
    """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::

        @dependency.requires('foo_api', 'bar_api')
        class FooBarClient:
            def __init__(self):
                ...

            ...

        client = FooBarClient()

    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
    beforehand.

    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."""
        self.__wrapped_init__(*args, **kwargs)
        _process_dependencies(self)

    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


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
    providers are registered. If an object needs to be created after this
    call, it must not have circular dependencies.

    If any required dependencies are unresolvable, this function will raise an
    ``UnresolvableDependencyException``.

    Outside of this module, this function should be called with no arguments;
    the optional argument, ``__provider_name`` is used internally, and should
    be treated as an implementation detail.

    """
    new_providers = dict()
    if __provider_name:
        # A provider was registered, so take care of any objects depending on
        # it.
        targets = _future_dependencies.pop(__provider_name, [])

        for target in targets:
            setattr(target, __provider_name, get_provider(__provider_name))

        return

    # Resolve future dependencies, raises UnresolvableDependencyException if
    # there's no provider registered.
    try:
        for dependency, targets in _future_dependencies.copy().items():
            if dependency not in _REGISTRY:
                # a Class was registered that could fulfill the dependency, but
                # it has not yet been initialized.
                factory = _factories.get(dependency)
                if factory:
                    provider = factory()
                    new_providers[dependency] = provider
                else:
                    raise UnresolvableDependencyException(dependency, targets)

            for target in targets:
                setattr(target, dependency, get_provider(dependency))
    finally:
        _future_dependencies.clear()
    return new_providers


def reset():
    """Reset the registry of providers.

    This is useful for unit testing to ensure that tests don't use providers
    from previous tests.
    """

    _REGISTRY.clear()
    _future_dependencies.clear()

3.用法

keystone/tests/unit/common/test_injection.py

class TestDependencyInjection(unit.BaseTestCase):
    def setUp(self):
        super(TestDependencyInjection, self).setUp()
        dependency.reset()
        self.addCleanup(dependency.reset)

    def test_dependency_injection(self):
        class Interface(object):
            def do_work(self):
                assert False

        @dependency.provider('first_api')
        class FirstImplementation(Interface):
            def do_work(self):
                return True

        @dependency.provider('second_api')
        class SecondImplementation(Interface):
            def do_work(self):
                return True

        @dependency.requires('first_api', 'second_api')
        class Consumer(object):
            def do_work_with_dependencies(self):
                assert self.first_api.do_work()
                assert self.second_api.do_work()

        # initialize dependency providers
        first_api = FirstImplementation()
        second_api = SecondImplementation()

        # ... sometime later, initialize a dependency consumer
        consumer = Consumer()

        # the expected dependencies should be available to the consumer
        self.assertIs(consumer.first_api, first_api)
        self.assertIs(consumer.second_api, second_api)
        self.assertIsInstance(consumer.first_api, Interface)
        self.assertIsInstance(consumer.second_api, Interface)
        consumer.do_work_with_dependencies()

============================================================ END ===================================================================

And now for something completely unrelated...

     今天天气很好,年前的最后一个周末,没事在公司晒着太阳,听着《老人与海》,睡了一觉,

     可是心情却很沮丧,

     因为2016年实在是太黑暗了,好在快要过去了,一切都会好的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值