# uncompyle6 version 3.3.5
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.7.3 (default, Apr 24 2019, 15:29:51) [MSC v.1915 64 bit (AMD64)]
# Embedded file name: c:\Jenkins\live\output\win_64_static\Release\python-bundle\MIDI Remote Scripts\_Framework\Dependency.py
# Compiled at: 2018-11-30 15:48:11
"""
Dependency injection framework.

The framework provides lously coupled passing of dependencies from
providers to the objects that require them.

Dependencies are identified by keys, that are valid Python
identifiers.  Dependencies are provided via accessor functions, that
in general will be called whenever they are needed.
"""
from __future__ import absolute_import, print_function, unicode_literals
__all__ = ('inject', 'depends', 'dependency')
from functools import wraps
from .Util import union

class DependencyError(Exception):
    pass


class InjectionRegistry(object):

    def __init__(self, parent=None, *a, **k):
        super(InjectionRegistry, self).__init__(*a, **k)
        self._key_registry = {}

    def register_key(self, key, injector):
        self._key_registry.setdefault(key, []).append(injector)

    def unregister_key(self, key, injector):
        self._key_registry[key].remove(injector)
        if not self._key_registry[key]:
            del self._key_registry[key]

    def get(self, key, default=None):
        try:
            return self._key_registry[key][(-1)].provides[key]
        except KeyError:
            return default


_global_injection_registry = InjectionRegistry()

def get_dependency_for(obj, name, default=None):
    accessor = _global_injection_registry.get(name, default)
    if accessor is not None:
        return accessor()
    else:
        raise DependencyError(b'Required dependency %s not provided for %s' % (
         name, str(obj)))
        return


class dependency(object):
    """
    Data descriptor that provides a given dependency looking as an
    attribute.  The depedency is specified as a keyword parameter,
    whose value can be a default accessor or None.  The attribute only
    tries to fetch the dependency on demand when needed.  Example::

         class HttpServer(object):
             connection_port = dependency(http_port = const(80))

         server = HttpServer()
         assert server.connection_port == 80
         with inject(connection_port = const(8000)).everywhere():
             assert server.connection_port == 8000
    """

    def __init__(self, **k):
        assert len(k) == 1
        self._dependency_name, self._dependency_default = k.items()[0]

    def __get__(self, obj, cls=None):
        if obj is None:
            obj = cls
        return get_dependency_for(obj, self._dependency_name, self._dependency_default)


def depends(**dependencies):
    """
    Decorates a method where dependencies are passed as keyword
    parameters.  Dependencies are specified as keywords with an
    optional accessor function or None if required.  Dependencies can
    be injected or passed directly as keyword parameters.  Example::

        class HttpServer(object):
            @depends(http_port = const(80))
            def listen(http_port = None):
                print "Listening on", http_port

        server = HttpServer()
        server.listen()
        server.listen(http_port = 8000)
        with inject(http_port = const(8000)).everywhere():
            server.listen()

    Produces the output::

        Listening on port 80
        Listening on port 8000
        Listening on port 8000
    """

    def decorator(func):

        @wraps(func)
        def wrapper(self, *a, **explicit):
            deps = dict([ (k, get_dependency_for(self, k, v)) for k, v in dependencies.iteritems() if k not in explicit
                        ])
            return func(self, *a, **union(deps, explicit))

        return wrapper

    return decorator


class Injector(object):

    @property
    def provides(self):
        return {}

    def register(self):
        pass

    def unregister(self):
        pass

    def __enter__(self):
        self.register()
        return self

    def __exit__(self, *a):
        self.unregister()


class RegistryInjector(Injector):

    def __init__(self, provides=None, registry=None, *a, **k):
        super(Injector, self).__init__(*a, **k)
        self._provides_dict = provides
        self._registry = registry

    @property
    def provides(self):
        return self._provides_dict

    def register(self):
        registry = self._registry
        for k in self._provides_dict:
            registry.register_key(k, self)

    def unregister(self):
        registry = self._registry
        for k in self._provides_dict:
            registry.unregister_key(k, self)


class InjectionFactory(object):

    def __init__(self, provides=None, *a, **k):
        super(InjectionFactory, self).__init__(*a, **k)
        self._provides_dict = provides

    def everywhere(self):
        return RegistryInjector(provides=self._provides_dict, registry=_global_injection_registry)

    into_object = NotImplemented
    into_class = NotImplemented


def inject(**k):
    """
    Inject returns a InjectorFactory that can generate Injectors to
    inject the provided keys at different levels.  The values to
    inject are specified as keyword parameters mapping keys to given
    nullary callables that will be used to access the dependency when
    needed.
    """
    return InjectionFactory(k)