root/trunk/twisted/python/components.py

Revision 33659, 14.3 KB (checked in by thijs, 3 months ago)

Merge impl-components-5034: Remove backwardsCompatImplements and fixClassImplements from twisted.python.components, deprecated in 2006.

Author: thijs
Reviewer: jesstess, itamar
Fixes: #5034

Line 
1# -*- test-case-name: twisted.python.test.test_components -*-
2# Copyright (c) Twisted Matrix Laboratories.
3# See LICENSE for details.
4
5
6"""
7Component architecture for Twisted, based on Zope3 components.
8
9Using the Zope3 API directly is strongly recommended. Everything
10you 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
23L{twisted.python.components.registerAdapter} from this module may be used to
24add to Twisted's global adapter registry.
25
26L{twisted.python.components.proxyForInterface} is a factory for classes
27which allow access to only the parts of another class defined by a specified
28interface.
29"""
30
31# zope3 imports
32from zope.interface import interface, declarations
33from zope.interface.adapter import AdapterRegistry
34
35# twisted imports
36from twisted.python import reflect
37from twisted.persisted import styles
38
39
40
41# Twisted's global adapter registry
42globalRegistry = AdapterRegistry()
43
44# Attribute that registerAdapter looks at. Is this supposed to be public?
45ALLOW_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.
50if 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('')
57else:
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
66def 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
90def 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
105def _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
125def _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
138def getRegistry():
139    """Returns the Twisted global
140    C{zope.interface.adapter.AdapterRegistry} instance.
141    """
142    return globalRegistry
143
144# FIXME: deprecate attribute somehow?
145CannotAdapt = TypeError
146
147class 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
190class 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
305class 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
318def 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
354class _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
386class _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]
Note: See TracBrowser for help on using the browser.