Imported Upstream version 12.1.0
[contrib/python-twisted.git] / twisted / python / components.py
1 # -*- test-case-name: twisted.python.test.test_components -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """
7 Component architecture for Twisted, based on Zope3 components.
8
9 Using the Zope3 API directly is strongly recommended. Everything
10 you need is in the top-level of the zope.interface package, e.g.::
11
12    from zope.interface import Interface, implements
13
14    class IFoo(Interface):
15        pass
16
17    class Foo:
18        implements(IFoo)
19
20    print IFoo.implementedBy(Foo) # True
21    print IFoo.providedBy(Foo()) # True
22
23 L{twisted.python.components.registerAdapter} from this module may be used to
24 add to Twisted's global adapter registry. 
25
26 L{twisted.python.components.proxyForInterface} is a factory for classes
27 which allow access to only the parts of another class defined by a specified
28 interface.
29 """
30
31 # zope3 imports
32 from zope.interface import interface, declarations
33 from zope.interface.adapter import AdapterRegistry
34
35 # twisted imports
36 from twisted.python import reflect
37 from twisted.persisted import styles
38
39
40
41 # Twisted's global adapter registry
42 globalRegistry = AdapterRegistry()
43
44 # Attribute that registerAdapter looks at. Is this supposed to be public?
45 ALLOW_DUPLICATES = 0
46
47 # Define a function to find the registered adapter factory, using either a
48 # version of Zope Interface which has the `registered' method or an older
49 # version which does not.
50 if getattr(AdapterRegistry, 'registered', None) is None:
51     def _registered(registry, required, provided):
52         """
53         Return the adapter factory for the given parameters in the given
54         registry, or None if there is not one.
55         """
56         return registry.get(required).selfImplied.get(provided, {}).get('')
57 else:
58     def _registered(registry, required, provided):
59         """
60         Return the adapter factory for the given parameters in the given
61         registry, or None if there is not one.
62         """
63         return registry.registered([required], provided)
64
65
66 def registerAdapter(adapterFactory, origInterface, *interfaceClasses):
67     """Register an adapter class.
68
69     An adapter class is expected to implement the given interface, by
70     adapting instances implementing 'origInterface'. An adapter class's
71     __init__ method should accept one parameter, an instance implementing
72     'origInterface'.
73     """
74     self = globalRegistry
75     assert interfaceClasses, "You need to pass an Interface"
76     global ALLOW_DUPLICATES
77
78     # deal with class->interface adapters:
79     if not isinstance(origInterface, interface.InterfaceClass):
80         origInterface = declarations.implementedBy(origInterface)
81
82     for interfaceClass in interfaceClasses:
83         factory = _registered(self, origInterface, interfaceClass)
84         if factory is not None and not ALLOW_DUPLICATES:
85             raise ValueError("an adapter (%s) was already registered." % (factory, ))
86     for interfaceClass in interfaceClasses:
87         self.register([origInterface], interfaceClass, '', adapterFactory)
88
89
90 def getAdapterFactory(fromInterface, toInterface, default):
91     """Return registered adapter for a given class and interface.
92
93     Note that is tied to the *Twisted* global registry, and will
94     thus not find adapters registered elsewhere.
95     """
96     self = globalRegistry
97     if not isinstance(fromInterface, interface.InterfaceClass):
98         fromInterface = declarations.implementedBy(fromInterface)
99     factory = self.lookup1(fromInterface, toInterface)
100     if factory is None:
101         factory = default
102     return factory
103
104
105 def _addHook(registry):
106     """
107     Add an adapter hook which will attempt to look up adapters in the given
108     registry.
109
110     @type registry: L{zope.interface.adapter.AdapterRegistry}
111
112     @return: The hook which was added, for later use with L{_removeHook}.
113     """
114     lookup = registry.lookup1
115     def _hook(iface, ob):
116         factory = lookup(declarations.providedBy(ob), iface)
117         if factory is None:
118             return None
119         else:
120             return factory(ob)
121     interface.adapter_hooks.append(_hook)
122     return _hook
123
124
125 def _removeHook(hook):
126     """
127     Remove a previously added adapter hook.
128
129     @param hook: An object previously returned by a call to L{_addHook}.  This
130         will be removed from the list of adapter hooks.
131     """
132     interface.adapter_hooks.remove(hook)
133
134 # add global adapter lookup hook for our newly created registry
135 _addHook(globalRegistry)
136
137
138 def getRegistry():
139     """Returns the Twisted global
140     C{zope.interface.adapter.AdapterRegistry} instance.
141     """
142     return globalRegistry
143
144 # FIXME: deprecate attribute somehow?
145 CannotAdapt = TypeError
146
147 class Adapter:
148     """I am the default implementation of an Adapter for some interface.
149
150     This docstring contains a limerick, by popular demand::
151
152         Subclassing made Zope and TR
153         much harder to work with by far.
154             So before you inherit,
155             be sure to declare it
156         Adapter, not PyObject*
157
158     @cvar temporaryAdapter: If this is True, the adapter will not be
159           persisted on the Componentized.
160     @cvar multiComponent: If this adapter is persistent, should it be
161           automatically registered for all appropriate interfaces.
162     """
163
164     # These attributes are used with Componentized.
165
166     temporaryAdapter = 0
167     multiComponent = 1
168
169     def __init__(self, original):
170         """Set my 'original' attribute to be the object I am adapting.
171         """
172         self.original = original
173
174     def __conform__(self, interface):
175         """
176         I forward __conform__ to self.original if it has it, otherwise I
177         simply return None.
178         """
179         if hasattr(self.original, "__conform__"):
180             return self.original.__conform__(interface)
181         return None
182
183     def isuper(self, iface, adapter):
184         """
185         Forward isuper to self.original
186         """
187         return self.original.isuper(iface, adapter)
188
189
190 class Componentized(styles.Versioned):
191     """I am a mixin to allow you to be adapted in various ways persistently.
192
193     I define a list of persistent adapters.  This is to allow adapter classes
194     to store system-specific state, and initialized on demand.  The
195     getComponent method implements this.  You must also register adapters for
196     this class for the interfaces that you wish to pass to getComponent.
197
198     Many other classes and utilities listed here are present in Zope3; this one
199     is specific to Twisted.
200     """
201
202     persistenceVersion = 1
203
204     def __init__(self):
205         self._adapterCache = {}
206
207     def locateAdapterClass(self, klass, interfaceClass, default):
208         return getAdapterFactory(klass, interfaceClass, default)
209
210     def setAdapter(self, interfaceClass, adapterClass):
211         self.setComponent(interfaceClass, adapterClass(self))
212
213     def addAdapter(self, adapterClass, ignoreClass=0):
214         """Utility method that calls addComponent.  I take an adapter class and
215         instantiate it with myself as the first argument.
216
217         @return: The adapter instantiated.
218         """
219         adapt = adapterClass(self)
220         self.addComponent(adapt, ignoreClass)
221         return adapt
222
223     def setComponent(self, interfaceClass, component):
224         """
225         """
226         self._adapterCache[reflect.qual(interfaceClass)] = component
227
228     def addComponent(self, component, ignoreClass=0):
229         """
230         Add a component to me, for all appropriate interfaces.
231
232         In order to determine which interfaces are appropriate, the component's
233         provided interfaces will be scanned.
234
235         If the argument 'ignoreClass' is True, then all interfaces are
236         considered appropriate.
237
238         Otherwise, an 'appropriate' interface is one for which its class has
239         been registered as an adapter for my class according to the rules of
240         getComponent.
241
242         @return: the list of appropriate interfaces
243         """
244         for iface in declarations.providedBy(component):
245             if (ignoreClass or
246                 (self.locateAdapterClass(self.__class__, iface, None)
247                  == component.__class__)):
248                 self._adapterCache[reflect.qual(iface)] = component
249
250     def unsetComponent(self, interfaceClass):
251         """Remove my component specified by the given interface class."""
252         del self._adapterCache[reflect.qual(interfaceClass)]
253
254     def removeComponent(self, component):
255         """
256         Remove the given component from me entirely, for all interfaces for which
257         it has been registered.
258
259         @return: a list of the interfaces that were removed.
260         """
261         l = []
262         for k, v in self._adapterCache.items():
263             if v is component:
264                 del self._adapterCache[k]
265                 l.append(reflect.namedObject(k))
266         return l
267
268     def getComponent(self, interface, default=None):
269         """Create or retrieve an adapter for the given interface.
270
271         If such an adapter has already been created, retrieve it from the cache
272         that this instance keeps of all its adapters.  Adapters created through
273         this mechanism may safely store system-specific state.
274
275         If you want to register an adapter that will be created through
276         getComponent, but you don't require (or don't want) your adapter to be
277         cached and kept alive for the lifetime of this Componentized object,
278         set the attribute 'temporaryAdapter' to True on your adapter class.
279
280         If you want to automatically register an adapter for all appropriate
281         interfaces (with addComponent), set the attribute 'multiComponent' to
282         True on your adapter class.
283         """
284         k = reflect.qual(interface)
285         if self._adapterCache.has_key(k):
286             return self._adapterCache[k]
287         else:
288             adapter = interface.__adapt__(self)
289             if adapter is not None and not (
290                 hasattr(adapter, "temporaryAdapter") and
291                 adapter.temporaryAdapter):
292                 self._adapterCache[k] = adapter
293                 if (hasattr(adapter, "multiComponent") and
294                     adapter.multiComponent):
295                     self.addComponent(adapter)
296             if adapter is None:
297                 return default
298             return adapter
299
300
301     def __conform__(self, interface):
302         return self.getComponent(interface)
303
304
305 class ReprableComponentized(Componentized):
306     def __init__(self):
307         Componentized.__init__(self)
308
309     def __repr__(self):
310         from cStringIO import StringIO
311         from pprint import pprint
312         sio = StringIO()
313         pprint(self._adapterCache, sio)
314         return sio.getvalue()
315
316
317
318 def proxyForInterface(iface, originalAttribute='original'):
319     """
320     Create a class which proxies all method calls which adhere to an interface
321     to another provider of that interface.
322
323     This function is intended for creating specialized proxies. The typical way
324     to use it is by subclassing the result::
325
326       class MySpecializedProxy(proxyForInterface(IFoo)):
327           def someInterfaceMethod(self, arg):
328               if arg == 3:
329                   return 3
330               return self.original.someInterfaceMethod(arg)
331
332     @param iface: The Interface to which the resulting object will conform, and
333         which the wrapped object must provide.
334
335     @param originalAttribute: name of the attribute used to save the original
336         object in the resulting class. Default to C{original}.
337     @type originalAttribute: C{str}
338
339     @return: A class whose constructor takes the original object as its only
340         argument. Constructing the class creates the proxy.
341     """
342     def __init__(self, original):
343         setattr(self, originalAttribute, original)
344     contents = {"__init__": __init__}
345     for name in iface:
346         contents[name] = _ProxyDescriptor(name, originalAttribute)
347     proxy = type("(Proxy for %s)"
348                  % (reflect.qual(iface),), (object,), contents)
349     declarations.classImplements(proxy, iface)
350     return proxy
351
352
353
354 class _ProxiedClassMethod(object):
355     """
356     A proxied class method.
357
358     @ivar methodName: the name of the method which this should invoke when
359         called.
360     @type methodName: C{str}
361
362     @ivar originalAttribute: name of the attribute of the proxy where the
363         original object is stored.
364     @type orginalAttribute: C{str}
365     """
366     def __init__(self, methodName, originalAttribute):
367         self.methodName = methodName
368         self.originalAttribute = originalAttribute
369
370
371     def __call__(self, oself, *args, **kw):
372         """
373         Invoke the specified L{methodName} method of the C{original} attribute
374         for proxyForInterface.
375
376         @param oself: an instance of a L{proxyForInterface} object.
377
378         @return: the result of the underlying method.
379         """
380         original = getattr(oself, self.originalAttribute)
381         actualMethod = getattr(original, self.methodName)
382         return actualMethod(*args, **kw)
383
384
385
386 class _ProxyDescriptor(object):
387     """
388     A descriptor which will proxy attribute access, mutation, and
389     deletion to the L{original} attribute of the object it is being accessed
390     from.
391
392     @ivar attributeName: the name of the attribute which this descriptor will
393         retrieve from instances' C{original} attribute.
394     @type attributeName: C{str}
395
396     @ivar originalAttribute: name of the attribute of the proxy where the
397         original object is stored.
398     @type orginalAttribute: C{str}
399     """
400     def __init__(self, attributeName, originalAttribute):
401         self.attributeName = attributeName
402         self.originalAttribute = originalAttribute
403
404
405     def __get__(self, oself, type=None):
406         """
407         Retrieve the C{self.attributeName} property from L{oself}.
408         """
409         if oself is None:
410             return _ProxiedClassMethod(self.attributeName,
411                                        self.originalAttribute)
412         original = getattr(oself, self.originalAttribute)
413         return getattr(original, self.attributeName)
414
415
416     def __set__(self, oself, value):
417         """
418         Set the C{self.attributeName} property of L{oself}.
419         """
420         original = getattr(oself, self.originalAttribute)
421         setattr(original, self.attributeName, value)
422
423
424     def __delete__(self, oself):
425         """
426         Delete the C{self.attributeName} property of L{oself}.
427         """
428         original = getattr(oself, self.originalAttribute)
429         delattr(original, self.attributeName)
430
431
432
433 __all__ = [
434     # Sticking around:
435     "registerAdapter", "getAdapterFactory",
436     "Adapter", "Componentized", "ReprableComponentized", "getRegistry",
437     "proxyForInterface",
438 ]