1
# -*- test-case-name: twisted.python.test.test_components -*-
2
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
3
# See LICENSE for details.
7
Component architecture for Twisted, based on Zope3 components.
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.::
12
from zope.interface import Interface, implements
14
class IFoo(Interface):
20
print IFoo.implementedBy(Foo) # True
21
print IFoo.providedBy(Foo()) # True
23
L{twisted.python.components.registerAdapter} from this module may be used to
24
add to Twisted's global adapter registry.
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
35
from zope.interface import interface, declarations
36
from zope.interface.adapter import AdapterRegistry
39
from twisted.python import reflect
40
from twisted.persisted import styles
43
class ComponentsDeprecationWarning(DeprecationWarning):
45
Nothing emits this warning anymore.
49
# Twisted's global adapter registry
50
globalRegistry = AdapterRegistry()
52
# Attribute that registerAdapter looks at. Is this supposed to be public?
55
# Define a function to find the registered adapter factory, using either a
56
# version of Zope Interface which has the `registered' method or an older
57
# version which does not.
58
if getattr(AdapterRegistry, 'registered', None) is None:
59
def _registered(registry, required, provided):
61
Return the adapter factory for the given parameters in the given
62
registry, or None if there is not one.
64
return registry.get(required).selfImplied.get(provided, {}).get('')
66
def _registered(registry, required, provided):
68
Return the adapter factory for the given parameters in the given
69
registry, or None if there is not one.
71
return registry.registered([required], provided)
74
def registerAdapter(adapterFactory, origInterface, *interfaceClasses):
75
"""Register an adapter class.
77
An adapter class is expected to implement the given interface, by
78
adapting instances implementing 'origInterface'. An adapter class's
79
__init__ method should accept one parameter, an instance implementing
83
assert interfaceClasses, "You need to pass an Interface"
84
global ALLOW_DUPLICATES
86
# deal with class->interface adapters:
87
if not isinstance(origInterface, interface.InterfaceClass):
88
origInterface = declarations.implementedBy(origInterface)
90
for interfaceClass in interfaceClasses:
91
factory = _registered(self, origInterface, interfaceClass)
92
if factory is not None and not ALLOW_DUPLICATES:
93
raise ValueError("an adapter (%s) was already registered." % (factory, ))
94
for interfaceClass in interfaceClasses:
95
self.register([origInterface], interfaceClass, '', adapterFactory)
98
def getAdapterFactory(fromInterface, toInterface, default):
99
"""Return registered adapter for a given class and interface.
101
Note that is tied to the *Twisted* global registry, and will
102
thus not find adapters registered elsewhere.
104
self = globalRegistry
105
if not isinstance(fromInterface, interface.InterfaceClass):
106
fromInterface = declarations.implementedBy(fromInterface)
107
factory = self.lookup1(fromInterface, toInterface)
113
# add global adapter lookup hook for our newly created registry
114
def _hook(iface, ob, lookup=globalRegistry.lookup1):
115
factory = lookup(declarations.providedBy(ob), iface)
120
interface.adapter_hooks.append(_hook)
122
## backwardsCompatImplements and fixClassImplements should probably stick around for another
123
## release cycle. No harm doing so in any case.
125
def backwardsCompatImplements(klass):
128
Does nothing. Previously handled backwards compat from a
129
zope.interface using class to a class wanting old twisted
130
components interface behaviors.
132
warnings.warn("components.backwardsCompatImplements doesn't do anything in Twisted 2.3, stop calling it.", ComponentsDeprecationWarning, stacklevel=2)
134
def fixClassImplements(klass):
137
Does nothing. Previously converted class from __implements__ to
140
warnings.warn("components.fixClassImplements doesn't do anything in Twisted 2.3, stop calling it.", ComponentsDeprecationWarning, stacklevel=2)
144
"""Returns the Twisted global
145
C{zope.interface.adapter.AdapterRegistry} instance.
147
return globalRegistry
149
# FIXME: deprecate attribute somehow?
150
CannotAdapt = TypeError
153
"""I am the default implementation of an Adapter for some interface.
155
This docstring contains a limerick, by popular demand::
157
Subclassing made Zope and TR
158
much harder to work with by far.
159
So before you inherit,
160
be sure to declare it
161
Adapter, not PyObject*
163
@cvar temporaryAdapter: If this is True, the adapter will not be
164
persisted on the Componentized.
165
@cvar multiComponent: If this adapter is persistent, should it be
166
automatically registered for all appropriate interfaces.
169
# These attributes are used with Componentized.
174
def __init__(self, original):
175
"""Set my 'original' attribute to be the object I am adapting.
177
self.original = original
179
def __conform__(self, interface):
181
I forward __conform__ to self.original if it has it, otherwise I
184
if hasattr(self.original, "__conform__"):
185
return self.original.__conform__(interface)
188
def isuper(self, iface, adapter):
190
Forward isuper to self.original
192
return self.original.isuper(iface, adapter)
195
class Componentized(styles.Versioned):
196
"""I am a mixin to allow you to be adapted in various ways persistently.
198
I define a list of persistent adapters. This is to allow adapter classes
199
to store system-specific state, and initialized on demand. The
200
getComponent method implements this. You must also register adapters for
201
this class for the interfaces that you wish to pass to getComponent.
203
Many other classes and utilities listed here are present in Zope3; this one
204
is specific to Twisted.
207
persistenceVersion = 1
210
self._adapterCache = {}
212
def locateAdapterClass(self, klass, interfaceClass, default):
213
return getAdapterFactory(klass, interfaceClass, default)
215
def setAdapter(self, interfaceClass, adapterClass):
216
self.setComponent(interfaceClass, adapterClass(self))
218
def addAdapter(self, adapterClass, ignoreClass=0):
219
"""Utility method that calls addComponent. I take an adapter class and
220
instantiate it with myself as the first argument.
222
@return: The adapter instantiated.
224
adapt = adapterClass(self)
225
self.addComponent(adapt, ignoreClass)
228
def setComponent(self, interfaceClass, component):
231
self._adapterCache[reflect.qual(interfaceClass)] = component
233
def addComponent(self, component, ignoreClass=0):
235
Add a component to me, for all appropriate interfaces.
237
In order to determine which interfaces are appropriate, the component's
238
provided interfaces will be scanned.
240
If the argument 'ignoreClass' is True, then all interfaces are
241
considered appropriate.
243
Otherwise, an 'appropriate' interface is one for which its class has
244
been registered as an adapter for my class according to the rules of
247
@return: the list of appropriate interfaces
249
for iface in declarations.providedBy(component):
251
(self.locateAdapterClass(self.__class__, iface, None)
252
== component.__class__)):
253
self._adapterCache[reflect.qual(iface)] = component
255
def unsetComponent(self, interfaceClass):
256
"""Remove my component specified by the given interface class."""
257
del self._adapterCache[reflect.qual(interfaceClass)]
259
def removeComponent(self, component):
261
Remove the given component from me entirely, for all interfaces for which
262
it has been registered.
264
@return: a list of the interfaces that were removed.
267
for k, v in self._adapterCache.items():
269
del self._adapterCache[k]
270
l.append(reflect.namedObject(k))
273
def getComponent(self, interface, default=None):
274
"""Create or retrieve an adapter for the given interface.
276
If such an adapter has already been created, retrieve it from the cache
277
that this instance keeps of all its adapters. Adapters created through
278
this mechanism may safely store system-specific state.
280
If you want to register an adapter that will be created through
281
getComponent, but you don't require (or don't want) your adapter to be
282
cached and kept alive for the lifetime of this Componentized object,
283
set the attribute 'temporaryAdapter' to True on your adapter class.
285
If you want to automatically register an adapter for all appropriate
286
interfaces (with addComponent), set the attribute 'multiComponent' to
287
True on your adapter class.
289
k = reflect.qual(interface)
290
if self._adapterCache.has_key(k):
291
return self._adapterCache[k]
293
adapter = interface.__adapt__(self)
294
if adapter is not None and not (
295
hasattr(adapter, "temporaryAdapter") and
296
adapter.temporaryAdapter):
297
self._adapterCache[k] = adapter
298
if (hasattr(adapter, "multiComponent") and
299
adapter.multiComponent):
300
self.addComponent(adapter)
306
def __conform__(self, interface):
307
return self.getComponent(interface)
310
class ReprableComponentized(Componentized):
312
Componentized.__init__(self)
315
from cStringIO import StringIO
316
from pprint import pprint
318
pprint(self._adapterCache, sio)
319
return sio.getvalue()
323
def proxyForInterface(iface, originalAttribute='original'):
325
Create a class which proxies all method calls which adhere to an interface
326
to another provider of that interface.
328
This function is intended for creating specialized proxies. The typical way
329
to use it is by subclassing the result::
331
class MySpecializedProxy(proxyForInterface(IFoo)):
332
def someInterfaceMethod(self, arg):
335
return self.original.someInterfaceMethod(arg)
337
@param iface: The Interface to which the resulting object will conform, and
338
which the wrapped object must provide.
340
@param originalAttribute: name of the attribute used to save the original
341
object in the resulting class. Default to C{original}.
342
@type originalAttribute: C{str}
344
@return: A class whose constructor takes the original object as its only
345
argument. Constructing the class creates the proxy.
347
def __init__(self, original):
348
setattr(self, originalAttribute, original)
349
contents = {"__init__": __init__}
351
contents[name] = _ProxyDescriptor(name, originalAttribute)
352
proxy = type("(Proxy for %s)"
353
% (reflect.qual(iface),), (object,), contents)
354
declarations.classImplements(proxy, iface)
359
class _ProxiedClassMethod(object):
361
A proxied class method.
363
@ivar methodName: the name of the method which this should invoke when
365
@type methodName: C{str}
367
@ivar originalAttribute: name of the attribute of the proxy where the
368
original object is stored.
369
@type orginalAttribute: C{str}
371
def __init__(self, methodName, originalAttribute):
372
self.methodName = methodName
373
self.originalAttribute = originalAttribute
376
def __call__(self, oself, *args, **kw):
378
Invoke the specified L{methodName} method of the C{original} attribute
379
for proxyForInterface.
381
@param oself: an instance of a L{proxyForInterface} object.
383
@return: the result of the underlying method.
385
original = getattr(oself, self.originalAttribute)
386
actualMethod = getattr(original, self.methodName)
387
return actualMethod(*args, **kw)
391
class _ProxyDescriptor(object):
393
A descriptor which will proxy attribute access, mutation, and
394
deletion to the L{original} attribute of the object it is being accessed
397
@ivar attributeName: the name of the attribute which this descriptor will
398
retrieve from instances' C{original} attribute.
399
@type attributeName: C{str}
401
@ivar originalAttribute: name of the attribute of the proxy where the
402
original object is stored.
403
@type orginalAttribute: C{str}
405
def __init__(self, attributeName, originalAttribute):
406
self.attributeName = attributeName
407
self.originalAttribute = originalAttribute
410
def __get__(self, oself, type=None):
412
Retrieve the C{self.attributeName} property from L{oself}.
415
return _ProxiedClassMethod(self.attributeName,
416
self.originalAttribute)
417
original = getattr(oself, self.originalAttribute)
418
return getattr(original, self.attributeName)
421
def __set__(self, oself, value):
423
Set the C{self.attributeName} property of L{oself}.
425
original = getattr(oself, self.originalAttribute)
426
setattr(original, self.attributeName, value)
429
def __delete__(self, oself):
431
Delete the C{self.attributeName} property of L{oself}.
433
original = getattr(oself, self.originalAttribute)
434
delattr(original, self.attributeName)
440
"ComponentsDeprecationWarning",
441
"registerAdapter", "getAdapterFactory",
442
"Adapter", "Componentized", "ReprableComponentized", "getRegistry",
446
"backwardsCompatImplements",
447
"fixClassImplements",