~washort/modules/main

« back to all changes in this revision

Viewing changes to components.py

  • Committer: Jean-Paul Calderone
  • Date: 2010-07-05 01:38:55 UTC
  • Revision ID: exarkun@top-20100705013855-bpou4qq0p7kublby
Adapt components.py for independent distribution

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
interface.
29
29
"""
30
30
 
31
 
# system imports
32
 
import warnings
33
 
 
34
31
# zope3 imports
35
32
from zope.interface import interface, declarations
36
33
from zope.interface.adapter import AdapterRegistry
37
34
 
38
 
# twisted imports
39
 
from twisted.python import reflect
40
 
from twisted.persisted import styles
41
 
 
42
 
 
43
 
class ComponentsDeprecationWarning(DeprecationWarning):
44
 
    """
45
 
    Nothing emits this warning anymore.
46
 
    """
47
 
 
48
 
 
49
 
# Twisted's global adapter registry
 
35
 
 
36
# The global adapter registry of the modules package
50
37
globalRegistry = AdapterRegistry()
51
38
 
52
39
# Attribute that registerAdapter looks at. Is this supposed to be public?
119
106
        return factory(ob)
120
107
interface.adapter_hooks.append(_hook)
121
108
 
122
 
## backwardsCompatImplements and fixClassImplements should probably stick around for another
123
 
## release cycle. No harm doing so in any case.
124
 
 
125
 
def backwardsCompatImplements(klass):
126
 
    """DEPRECATED.
127
 
 
128
 
    Does nothing. Previously handled backwards compat from a
129
 
    zope.interface using class to a class wanting old twisted
130
 
    components interface behaviors.
131
 
    """
132
 
    warnings.warn("components.backwardsCompatImplements doesn't do anything in Twisted 2.3, stop calling it.", ComponentsDeprecationWarning, stacklevel=2)
133
 
 
134
 
def fixClassImplements(klass):
135
 
    """DEPRECATED.
136
 
 
137
 
    Does nothing. Previously converted class from __implements__ to
138
 
    zope implementation.
139
 
    """
140
 
    warnings.warn("components.fixClassImplements doesn't do anything in Twisted 2.3, stop calling it.", ComponentsDeprecationWarning, stacklevel=2)
141
 
 
142
109
 
143
110
def getRegistry():
144
111
    """Returns the Twisted global
146
113
    """
147
114
    return globalRegistry
148
115
 
149
 
# FIXME: deprecate attribute somehow?
150
 
CannotAdapt = TypeError
151
 
 
152
 
class Adapter:
153
 
    """I am the default implementation of an Adapter for some interface.
154
 
 
155
 
    This docstring contains a limerick, by popular demand::
156
 
 
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*
162
 
 
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.
167
 
    """
168
 
 
169
 
    # These attributes are used with Componentized.
170
 
 
171
 
    temporaryAdapter = 0
172
 
    multiComponent = 1
173
 
 
174
 
    def __init__(self, original):
175
 
        """Set my 'original' attribute to be the object I am adapting.
176
 
        """
177
 
        self.original = original
178
 
 
179
 
    def __conform__(self, interface):
180
 
        """
181
 
        I forward __conform__ to self.original if it has it, otherwise I
182
 
        simply return None.
183
 
        """
184
 
        if hasattr(self.original, "__conform__"):
185
 
            return self.original.__conform__(interface)
186
 
        return None
187
 
 
188
 
    def isuper(self, iface, adapter):
189
 
        """
190
 
        Forward isuper to self.original
191
 
        """
192
 
        return self.original.isuper(iface, adapter)
193
 
 
194
 
 
195
 
class Componentized(styles.Versioned):
196
 
    """I am a mixin to allow you to be adapted in various ways persistently.
197
 
 
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.
202
 
 
203
 
    Many other classes and utilities listed here are present in Zope3; this one
204
 
    is specific to Twisted.
205
 
    """
206
 
 
207
 
    persistenceVersion = 1
208
 
 
209
 
    def __init__(self):
210
 
        self._adapterCache = {}
211
 
 
212
 
    def locateAdapterClass(self, klass, interfaceClass, default):
213
 
        return getAdapterFactory(klass, interfaceClass, default)
214
 
 
215
 
    def setAdapter(self, interfaceClass, adapterClass):
216
 
        self.setComponent(interfaceClass, adapterClass(self))
217
 
 
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.
221
 
 
222
 
        @return: The adapter instantiated.
223
 
        """
224
 
        adapt = adapterClass(self)
225
 
        self.addComponent(adapt, ignoreClass)
226
 
        return adapt
227
 
 
228
 
    def setComponent(self, interfaceClass, component):
229
 
        """
230
 
        """
231
 
        self._adapterCache[reflect.qual(interfaceClass)] = component
232
 
 
233
 
    def addComponent(self, component, ignoreClass=0):
234
 
        """
235
 
        Add a component to me, for all appropriate interfaces.
236
 
 
237
 
        In order to determine which interfaces are appropriate, the component's
238
 
        provided interfaces will be scanned.
239
 
 
240
 
        If the argument 'ignoreClass' is True, then all interfaces are
241
 
        considered appropriate.
242
 
 
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
245
 
        getComponent.
246
 
 
247
 
        @return: the list of appropriate interfaces
248
 
        """
249
 
        for iface in declarations.providedBy(component):
250
 
            if (ignoreClass or
251
 
                (self.locateAdapterClass(self.__class__, iface, None)
252
 
                 == component.__class__)):
253
 
                self._adapterCache[reflect.qual(iface)] = component
254
 
 
255
 
    def unsetComponent(self, interfaceClass):
256
 
        """Remove my component specified by the given interface class."""
257
 
        del self._adapterCache[reflect.qual(interfaceClass)]
258
 
 
259
 
    def removeComponent(self, component):
260
 
        """
261
 
        Remove the given component from me entirely, for all interfaces for which
262
 
        it has been registered.
263
 
 
264
 
        @return: a list of the interfaces that were removed.
265
 
        """
266
 
        l = []
267
 
        for k, v in self._adapterCache.items():
268
 
            if v is component:
269
 
                del self._adapterCache[k]
270
 
                l.append(reflect.namedObject(k))
271
 
        return l
272
 
 
273
 
    def getComponent(self, interface, default=None):
274
 
        """Create or retrieve an adapter for the given interface.
275
 
 
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.
279
 
 
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.
284
 
 
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.
288
 
        """
289
 
        k = reflect.qual(interface)
290
 
        if self._adapterCache.has_key(k):
291
 
            return self._adapterCache[k]
292
 
        else:
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)
301
 
            if adapter is None:
302
 
                return default
303
 
            return adapter
304
 
 
305
 
 
306
 
    def __conform__(self, interface):
307
 
        return self.getComponent(interface)
308
 
 
309
 
 
310
 
class ReprableComponentized(Componentized):
311
 
    def __init__(self):
312
 
        Componentized.__init__(self)
313
 
 
314
 
    def __repr__(self):
315
 
        from cStringIO import StringIO
316
 
        from pprint import pprint
317
 
        sio = StringIO()
318
 
        pprint(self._adapterCache, sio)
319
 
        return sio.getvalue()
320
 
 
321
 
 
322
 
 
323
 
def proxyForInterface(iface, originalAttribute='original'):
324
 
    """
325
 
    Create a class which proxies all method calls which adhere to an interface
326
 
    to another provider of that interface.
327
 
 
328
 
    This function is intended for creating specialized proxies. The typical way
329
 
    to use it is by subclassing the result::
330
 
 
331
 
      class MySpecializedProxy(proxyForInterface(IFoo)):
332
 
          def someInterfaceMethod(self, arg):
333
 
              if arg == 3:
334
 
                  return 3
335
 
              return self.original.someInterfaceMethod(arg)
336
 
 
337
 
    @param iface: The Interface to which the resulting object will conform, and
338
 
        which the wrapped object must provide.
339
 
 
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}
343
 
 
344
 
    @return: A class whose constructor takes the original object as its only
345
 
        argument. Constructing the class creates the proxy.
346
 
    """
347
 
    def __init__(self, original):
348
 
        setattr(self, originalAttribute, original)
349
 
    contents = {"__init__": __init__}
350
 
    for name in iface:
351
 
        contents[name] = _ProxyDescriptor(name, originalAttribute)
352
 
    proxy = type("(Proxy for %s)"
353
 
                 % (reflect.qual(iface),), (object,), contents)
354
 
    declarations.classImplements(proxy, iface)
355
 
    return proxy
356
 
 
357
 
 
358
 
 
359
 
class _ProxiedClassMethod(object):
360
 
    """
361
 
    A proxied class method.
362
 
 
363
 
    @ivar methodName: the name of the method which this should invoke when
364
 
        called.
365
 
    @type methodName: C{str}
366
 
 
367
 
    @ivar originalAttribute: name of the attribute of the proxy where the
368
 
        original object is stored.
369
 
    @type orginalAttribute: C{str}
370
 
    """
371
 
    def __init__(self, methodName, originalAttribute):
372
 
        self.methodName = methodName
373
 
        self.originalAttribute = originalAttribute
374
 
 
375
 
 
376
 
    def __call__(self, oself, *args, **kw):
377
 
        """
378
 
        Invoke the specified L{methodName} method of the C{original} attribute
379
 
        for proxyForInterface.
380
 
 
381
 
        @param oself: an instance of a L{proxyForInterface} object.
382
 
 
383
 
        @return: the result of the underlying method.
384
 
        """
385
 
        original = getattr(oself, self.originalAttribute)
386
 
        actualMethod = getattr(original, self.methodName)
387
 
        return actualMethod(*args, **kw)
388
 
 
389
 
 
390
 
 
391
 
class _ProxyDescriptor(object):
392
 
    """
393
 
    A descriptor which will proxy attribute access, mutation, and
394
 
    deletion to the L{original} attribute of the object it is being accessed
395
 
    from.
396
 
 
397
 
    @ivar attributeName: the name of the attribute which this descriptor will
398
 
        retrieve from instances' C{original} attribute.
399
 
    @type attributeName: C{str}
400
 
 
401
 
    @ivar originalAttribute: name of the attribute of the proxy where the
402
 
        original object is stored.
403
 
    @type orginalAttribute: C{str}
404
 
    """
405
 
    def __init__(self, attributeName, originalAttribute):
406
 
        self.attributeName = attributeName
407
 
        self.originalAttribute = originalAttribute
408
 
 
409
 
 
410
 
    def __get__(self, oself, type=None):
411
 
        """
412
 
        Retrieve the C{self.attributeName} property from L{oself}.
413
 
        """
414
 
        if oself is None:
415
 
            return _ProxiedClassMethod(self.attributeName,
416
 
                                       self.originalAttribute)
417
 
        original = getattr(oself, self.originalAttribute)
418
 
        return getattr(original, self.attributeName)
419
 
 
420
 
 
421
 
    def __set__(self, oself, value):
422
 
        """
423
 
        Set the C{self.attributeName} property of L{oself}.
424
 
        """
425
 
        original = getattr(oself, self.originalAttribute)
426
 
        setattr(original, self.attributeName, value)
427
 
 
428
 
 
429
 
    def __delete__(self, oself):
430
 
        """
431
 
        Delete the C{self.attributeName} property of L{oself}.
432
 
        """
433
 
        original = getattr(oself, self.originalAttribute)
434
 
        delattr(original, self.attributeName)
435
 
 
436
 
 
437
116
 
438
117
__all__ = [
439
118
    # Sticking around:
440
 
    "ComponentsDeprecationWarning",
441
119
    "registerAdapter", "getAdapterFactory",
442
 
    "Adapter", "Componentized", "ReprableComponentized", "getRegistry",
443
 
    "proxyForInterface",
444
 
 
445
 
    # Deprecated:
446
 
    "backwardsCompatImplements",
447
 
    "fixClassImplements",
 
120
    "getRegistry",
448
121
]