keystone/common/dependency.py

来源:互联网 发布:开个淘宝网店多少钱 编辑:程序博客网 时间:2024/06/15 05:00

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 tracebackfrom 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 wrapperdef _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 wrappeddef 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_providersdef 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