Package _Framework :: Module Dependency
[hide private]
[frames] | no frames]

Source Code for Module _Framework.Dependency

  1  #Embedded file name: /Users/versonator/Hudson/live/Projects/AppLive/Resources/MIDI Remote Scripts/_Framework/Dependency.py 
  2  """ 
  3  Dependency injection framework. 
  4   
  5  The framework provides lously coupled passing of dependencies from 
  6  providers to the objects that require them. 
  7   
  8  Dependencies are identified by keys, that are valid Python 
  9  identifiers.  Dependencies are provided via accessor functions, that 
 10  in general will be called whenever they are needed. 
 11  """ 
 12  __all__ = ('inject', 'depends', 'dependency') 
 13  from functools import wraps 
 14  from Util import union 
15 16 -class DependencyError(Exception):
17 pass
18
19 20 -class InjectionRegistry(object):
21
22 - def __init__(self, parent = None, *a, **k):
23 super(InjectionRegistry, self).__init__(*a, **k) 24 self._key_registry = {}
25
26 - def register_key(self, key, injector):
27 self._key_registry.setdefault(key, []).append(injector)
28
29 - def unregister_key(self, key, injector):
30 self._key_registry[key].remove(injector) 31 if not self._key_registry[key]: 32 del self._key_registry[key]
33
34 - def get(self, key, default = None):
35 try: 36 return self._key_registry[key][-1].provides[key] 37 except KeyError: 38 return default
39 40 41 _global_injection_registry = InjectionRegistry()
42 43 -def get_dependency_for(obj, name, default = None):
44 accessor = _global_injection_registry.get(name, default) 45 if accessor is not None: 46 return accessor() 47 else: 48 raise DependencyError('Required dependency %s not provided for %s' % (name, str(obj)))
49
50 51 -class dependency(object):
52 """ 53 Data descriptor that provides a given dependency looking as an 54 attribute. The depedency is specified as a keyword parameter, 55 whose value can be a default accessor or None. The attribute only 56 tries to fetch the dependency on deman when needed. Example:: 57 58 class HttpServer(object): 59 connection_port = dependency(http_port = const(80)) 60 61 server = HttpServer() 62 assert server.connection_port == 80 63 with inject(connection_port = const(8000)).everywhere(): 64 assert server.connection_port == 8000 65 """ 66
67 - def __init__(self, **k):
68 raise len(k) == 1 or AssertionError 69 self._dependency_name, self._dependency_default = k.items()[0]
70
71 - def __get__(self, obj, cls = None):
72 if obj is None: 73 obj = cls 74 return get_dependency_for(obj, self._dependency_name, self._dependency_default)
75
76 77 -def depends(**dependencies):
78 """ 79 Decorates a method where dependencies are passed as keyword 80 parameters. Dependencies are specified as keywords with an 81 optional accessor function or None if required. Dependencies can 82 be injected or passed directly as keyword parameters. Example:: 83 84 class HttpServer(object): 85 @depends(http_port = const(80)) 86 def listen(http_port = None): 87 print "Listening on", http_port 88 89 server = HttpServer() 90 server.listen() 91 server.listen(http_port = 8000) 92 with inject(http_port = const(8000)).everywhere(): 93 server.listen() 94 95 Produces the output:: 96 97 Listening on port 80 98 Listening on port 8000 99 Listening on port 8000 100 """ 101 102 def decorator(func): 103 104 @wraps(func) 105 def wrapper(self, *a, **explicit): 106 deps = dict([ (k, get_dependency_for(self, k, v)) for k, v in dependencies.iteritems() if k not in explicit ]) 107 return func(self, *a, **union(deps, explicit))
108 109 return wrapper 110 111 return decorator 112
113 114 -class Injector(object):
115 116 @property
117 - def provides(self):
118 return {}
119
120 - def register(self):
121 pass
122
123 - def unregister(self):
124 pass
125
126 - def __enter__(self):
127 self.register() 128 return self
129
130 - def __exit__(self, *a):
131 self.unregister()
132
133 134 -class RegistryInjector(Injector):
135
136 - def __init__(self, provides = None, registry = None, *a, **k):
137 super(Injector, self).__init__(*a, **k) 138 self._provides_dict = provides 139 self._registry = registry
140 141 @property
142 - def provides(self):
143 return self._provides_dict
144
145 - def register(self):
146 registry = self._registry 147 for k in self._provides_dict: 148 registry.register_key(k, self)
149
150 - def unregister(self):
151 registry = self._registry 152 for k in self._provides_dict: 153 registry.unregister_key(k, self)
154
155 156 -class InjectionFactory(object):
157
158 - def __init__(self, provides = None, *a, **k):
159 super(InjectionFactory, self).__init__(*a, **k) 160 self._provides_dict = provides
161
162 - def everywhere(self):
163 return RegistryInjector(provides=self._provides_dict, registry=_global_injection_registry)
164 165 into_object = NotImplemented 166 into_class = NotImplemented
167
168 169 -def inject(**k):
170 """ 171 Inject returns a InjectorFactory that can generate Injectors to 172 inject the provided keys at different levels. The values to 173 inject are specified as keyword parameters mapping keys to given 174 nullary callables that will be used to access the dependency when 175 needed. 176 """ 177 return InjectionFactory(k)
178