1
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
2
# See LICENSE for details.
6
Test cases for Twisted component architecture.
9
from zope.interface import Interface, implements, Attribute
11
from twisted.trial import unittest
12
from twisted.python import components
13
from twisted.python.components import proxyForInterface
16
class InterfacesTestCase(unittest.TestCase):
17
"""Test interfaces."""
19
class Compo(components.Componentized):
22
self.num = self.num + 1
25
class IAdept(Interface):
27
raise NotImplementedError()
29
class IElapsed(Interface):
35
class Adept(components.Adapter):
37
def __init__(self, orig):
40
def adaptorFunc(self):
41
self.num = self.num + 1
42
return self.num, self.original.inc()
44
class Elapsed(components.Adapter):
46
def elapsedFunc(self):
49
components.registerAdapter(Adept, Compo, IAdept)
50
components.registerAdapter(Elapsed, Compo, IElapsed)
52
class AComp(components.Componentized):
59
class ITest(Interface):
61
class ITest2(Interface):
63
class ITest3(Interface):
65
class ITest4(Interface):
67
class Test(components.Adapter):
68
implements(ITest, ITest3, ITest4)
69
def __init__(self, orig):
74
def __init__(self, orig):
77
components.registerAdapter(Test, AComp, ITest)
78
components.registerAdapter(Test, AComp, ITest3)
79
components.registerAdapter(Test2, AComp, ITest2)
84
class ComponentizedTestCase(unittest.TestCase):
85
"""Simple test case for caching in Componentized.
87
def testComponentized(self):
89
assert c.getComponent(IAdept).adaptorFunc() == (1, 1)
90
assert c.getComponent(IAdept).adaptorFunc() == (2, 2)
91
assert IElapsed(IAdept(c)).elapsedFunc() == 1
93
def testInheritanceAdaptation(self):
95
co1 = c.getComponent(ITest)
96
co2 = c.getComponent(ITest)
97
co3 = c.getComponent(ITest2)
98
co4 = c.getComponent(ITest2)
100
assert co3 is not co4
101
c.removeComponent(co1)
102
co5 = c.getComponent(ITest)
103
co6 = c.getComponent(ITest)
105
assert co1 is not co5
107
def testMultiAdapter(self):
109
co1 = c.getComponent(ITest)
110
co2 = c.getComponent(ITest2)
111
co3 = c.getComponent(ITest3)
112
co4 = c.getComponent(ITest4)
117
def test_getComponentDefaults(self):
119
Test that a default value specified to Componentized.getComponent if
120
there is no component for the requested interface.
122
componentized = components.Componentized()
124
self.assertIdentical(
125
componentized.getComponent(ITest, default),
127
self.assertIdentical(
128
componentized.getComponent(ITest, default=default),
130
self.assertIdentical(
131
componentized.getComponent(ITest),
136
class AdapterTestCase(unittest.TestCase):
139
def testAdapterGetComponent(self):
142
self.assertRaises(components.CannotAdapt, ITest, a)
143
self.assertEquals(ITest(a, None), None)
147
class IMeta(Interface):
150
class MetaAdder(components.Adapter):
153
return self.original.num + num
155
class BackwardsAdder(components.Adapter):
158
return self.original.num - num
161
def __init__(self, num):
171
class ComponentNumber(components.Componentized):
174
components.Componentized.__init__(self)
176
class ComponentMeta(components.Adapter):
178
def __init__(self, original):
179
components.Adapter.__init__(self, original)
180
self.num = self.original.num
182
class ComponentAdder(ComponentMeta):
187
class ComponentDoubler(ComponentMeta):
189
self.num += (num * 2)
190
return self.original.num
192
components.registerAdapter(MetaAdder, MetaNumber, IMeta)
193
components.registerAdapter(ComponentAdder, ComponentNumber, IMeta)
195
class IAttrX(Interface):
199
class IAttrXX(Interface):
208
class DoubleXAdapter:
210
def __init__(self, original):
211
self.original = original
213
return (self.original.x(), self.original.x())
214
def __cmp__(self, other):
215
return cmp(self.num, other.num)
217
components.registerAdapter(DoubleXAdapter, IAttrX, IAttrXX)
219
class TestMetaInterface(unittest.TestCase):
223
self.assertEquals(IMeta(n).add(1), 2)
225
def testComponentizedInteraction(self):
226
c = ComponentNumber()
229
self.assertEquals(IMeta(c).add(1), 3)
231
def testAdapterWithCmp(self):
232
# Make sure that a __cmp__ on an adapter doesn't break anything
233
xx = IAttrXX(Xcellent())
234
self.assertEqual(('x!', 'x!'), xx.xx())
237
class RegistrationTestCase(unittest.TestCase):
239
Tests for adapter registration.
241
def _registerAdapterForClassOrInterface(self, original):
242
adapter = lambda o: None
243
class TheInterface(Interface):
245
components.registerAdapter(adapter, original, TheInterface)
246
self.assertIdentical(
247
components.getAdapterFactory(original, TheInterface, None),
251
def test_registerAdapterForClass(self):
253
Test that an adapter from a class can be registered and then looked
256
class TheOriginal(object):
258
return self._registerAdapterForClassOrInterface(TheOriginal)
261
def test_registerAdapterForInterface(self):
263
Test that an adapter from an interface can be registered and then
266
class TheOriginal(Interface):
268
return self._registerAdapterForClassOrInterface(TheOriginal)
271
def _duplicateAdapterForClassOrInterface(self, original):
272
firstAdapter = lambda o: False
273
secondAdapter = lambda o: True
274
class TheInterface(Interface):
276
components.registerAdapter(firstAdapter, original, TheInterface)
279
components.registerAdapter,
280
secondAdapter, original, TheInterface)
281
# Make sure that the original adapter is still around as well
282
self.assertIdentical(
283
components.getAdapterFactory(original, TheInterface, None),
287
def test_duplicateAdapterForClass(self):
289
Test that attempting to register a second adapter from a class
290
raises the appropriate exception.
292
class TheOriginal(object):
294
return self._duplicateAdapterForClassOrInterface(TheOriginal)
297
def test_duplicateAdapterForInterface(self):
299
Test that attempting to register a second adapter from an interface
300
raises the appropriate exception.
302
class TheOriginal(Interface):
304
return self._duplicateAdapterForClassOrInterface(TheOriginal)
307
def _duplicateAdapterForClassOrInterfaceAllowed(self, original):
308
firstAdapter = lambda o: False
309
secondAdapter = lambda o: True
310
class TheInterface(Interface):
312
components.registerAdapter(firstAdapter, original, TheInterface)
313
components.ALLOW_DUPLICATES = True
315
components.registerAdapter(secondAdapter, original, TheInterface)
316
self.assertIdentical(
317
components.getAdapterFactory(original, TheInterface, None),
320
components.ALLOW_DUPLICATES = False
322
# It should be rejected again at this point
325
components.registerAdapter,
326
firstAdapter, original, TheInterface)
328
self.assertIdentical(
329
components.getAdapterFactory(original, TheInterface, None),
332
def test_duplicateAdapterForClassAllowed(self):
334
Test that when L{components.ALLOW_DUPLICATES} is set to a true
335
value, duplicate registrations from classes are allowed to override
336
the original registration.
338
class TheOriginal(object):
340
return self._duplicateAdapterForClassOrInterfaceAllowed(TheOriginal)
343
def test_duplicateAdapterForInterfaceAllowed(self):
345
Test that when L{components.ALLOW_DUPLICATES} is set to a true
346
value, duplicate registrations from interfaces are allowed to
347
override the original registration.
349
class TheOriginal(Interface):
351
return self._duplicateAdapterForClassOrInterfaceAllowed(TheOriginal)
354
def _multipleInterfacesForClassOrInterface(self, original):
355
adapter = lambda o: None
356
class FirstInterface(Interface):
358
class SecondInterface(Interface):
360
components.registerAdapter(adapter, original, FirstInterface, SecondInterface)
361
self.assertIdentical(
362
components.getAdapterFactory(original, FirstInterface, None),
364
self.assertIdentical(
365
components.getAdapterFactory(original, SecondInterface, None),
369
def test_multipleInterfacesForClass(self):
371
Test the registration of an adapter from a class to several
374
class TheOriginal(object):
376
return self._multipleInterfacesForClassOrInterface(TheOriginal)
379
def test_multipleInterfacesForInterface(self):
381
Test the registration of an adapter from an interface to several
384
class TheOriginal(Interface):
386
return self._multipleInterfacesForClassOrInterface(TheOriginal)
389
def _subclassAdapterRegistrationForClassOrInterface(self, original):
390
firstAdapter = lambda o: True
391
secondAdapter = lambda o: False
392
class TheSubclass(original):
394
class TheInterface(Interface):
396
components.registerAdapter(firstAdapter, original, TheInterface)
397
components.registerAdapter(secondAdapter, TheSubclass, TheInterface)
398
self.assertIdentical(
399
components.getAdapterFactory(original, TheInterface, None),
401
self.assertIdentical(
402
components.getAdapterFactory(TheSubclass, TheInterface, None),
406
def test_subclassAdapterRegistrationForClass(self):
408
Test that an adapter to a particular interface can be registered
409
from both a class and its subclass.
411
class TheOriginal(object):
413
return self._subclassAdapterRegistrationForClassOrInterface(TheOriginal)
416
def test_subclassAdapterRegistrationForInterface(self):
418
Test that an adapter to a particular interface can be registered
419
from both an interface and its subclass.
421
class TheOriginal(Interface):
423
return self._subclassAdapterRegistrationForClassOrInterface(TheOriginal)
427
class IProxiedInterface(Interface):
429
An interface class for use by L{proxyForInterface}.
432
ifaceAttribute = Attribute("""
433
An example declared attribute, which should be proxied.""")
437
A sample method which should be proxied.
440
class IProxiedSubInterface(IProxiedInterface):
442
An interface that derives from another for use with L{proxyForInterface}.
447
A different sample method which should be proxied.
452
class Yayable(object):
454
A provider of L{IProxiedInterface} which increments a counter for
455
every call to C{yay}.
457
@ivar yays: The number of times C{yay} has been called.
459
implements(IProxiedInterface)
465
def yay(self, *a, **kw):
467
Increment C{self.yays}.
470
self.yayArgs.append((a, kw))
474
class Booable(object):
476
An implementation of IProxiedSubInterface
478
implements(IProxiedSubInterface)
483
Mark the fact that 'yay' has been called.
490
Mark the fact that 'boo' has been called.1
496
class IMultipleMethods(Interface):
498
An interface with multiple methods.
503
The first method. Should return 1.
508
The second method. Should return 2.
513
class MultipleMethodImplementor(object):
515
A precise implementation of L{IMultipleMethods}.
533
class ProxyForInterfaceTests(unittest.TestCase):
535
Tests for L{proxyForInterface}.
538
def test_original(self):
540
Proxy objects should have an C{original} attribute which refers to the
541
original object passed to the constructor.
544
proxy = proxyForInterface(IProxiedInterface)(original)
545
self.assertIdentical(proxy.original, original)
548
def test_proxyMethod(self):
550
The class created from L{proxyForInterface} passes methods on an
551
interface to the object which is passed to its constructor.
553
klass = proxyForInterface(IProxiedInterface)
555
proxy = klass(yayable)
557
self.assertEquals(proxy.yay(), 2)
558
self.assertEquals(yayable.yays, 2)
561
def test_proxyAttribute(self):
563
Proxy objects should proxy declared attributes, but not other
567
yayable.ifaceAttribute = object()
568
proxy = proxyForInterface(IProxiedInterface)(yayable)
569
self.assertIdentical(proxy.ifaceAttribute, yayable.ifaceAttribute)
570
self.assertRaises(AttributeError, lambda: proxy.yays)
573
def test_proxySetAttribute(self):
575
The attributes that proxy objects proxy should be assignable and affect
579
proxy = proxyForInterface(IProxiedInterface)(yayable)
581
proxy.ifaceAttribute = thingy
582
self.assertIdentical(yayable.ifaceAttribute, thingy)
585
def test_proxyDeleteAttribute(self):
587
The attributes that proxy objects proxy should be deletable and affect
591
yayable.ifaceAttribute = None
592
proxy = proxyForInterface(IProxiedInterface)(yayable)
593
del proxy.ifaceAttribute
594
self.assertFalse(hasattr(yayable, 'ifaceAttribute'))
597
def test_multipleMethods(self):
599
[Regression test] The proxy should send its method calls to the correct
600
method, not the incorrect one.
602
multi = MultipleMethodImplementor()
603
proxy = proxyForInterface(IMultipleMethods)(multi)
604
self.assertEquals(proxy.methodOne(), 1)
605
self.assertEquals(proxy.methodTwo(), 2)
608
def test_subclassing(self):
610
It is possible to subclass the result of L{proxyForInterface}.
613
class SpecializedProxy(proxyForInterface(IProxiedInterface)):
615
A specialized proxy which can decrement the number of yays.
619
Decrement the number of yays.
621
self.original.yays -= 1
624
special = SpecializedProxy(yayable)
625
self.assertEquals(yayable.yays, 0)
627
self.assertEquals(yayable.yays, -1)
630
def test_proxyName(self):
632
The name of a proxy class indicates which interface it proxies.
634
proxy = proxyForInterface(IProxiedInterface)
638
"twisted.python.test.test_components.IProxiedInterface)")
641
def test_implements(self):
643
The resulting proxy implements the interface that it proxies.
645
proxy = proxyForInterface(IProxiedInterface)
646
self.assertTrue(IProxiedInterface.implementedBy(proxy))
649
def test_proxyDescriptorGet(self):
651
_ProxyDescriptor's __get__ method should return the appropriate
652
attribute of its argument's 'original' attribute if it is invoked with
653
an object. If it is invoked with None, it should return a false
654
class-method emulator instead.
656
For some reason, Python's documentation recommends to define
657
descriptors' __get__ methods with the 'type' parameter as optional,
658
despite the fact that Python itself never actually calls the descriptor
659
that way. This is probably do to support 'foo.__get__(bar)' as an
660
idiom. Let's make sure that the behavior is correct. Since we don't
661
actually use the 'type' argument at all, this test calls it the
662
idiomatic way to ensure that signature works; test_proxyInheritance
663
verifies the how-Python-actually-calls-it signature.
670
testObject = Sample()
671
fakeProxy.original = testObject
672
pd = components._ProxyDescriptor("hello", "original")
673
self.assertEquals(pd.__get__(fakeProxy), testObject.hello)
674
fakeClassMethod = pd.__get__(None)
675
fakeClassMethod(fakeProxy)
676
self.failUnless(testObject.called)
679
def test_proxyInheritance(self):
681
Subclasses of the class returned from L{proxyForInterface} should be
682
able to upcall methods by reference to their superclass, as any normal
685
class YayableWrapper(proxyForInterface(IProxiedInterface)):
687
This class does not override any functionality.
690
class EnhancedWrapper(YayableWrapper):
692
This class overrides the 'yay' method.
695
def yay(self, *a, **k):
696
self.wrappedYays += 1
697
return YayableWrapper.yay(self, *a, **k) + 7
700
wrapper = EnhancedWrapper(yayable)
701
self.assertEquals(wrapper.yay(3, 4, x=5, y=6), 8)
702
self.assertEquals(yayable.yayArgs,
703
[((3, 4), dict(x=5, y=6))])
706
def test_interfaceInheritance(self):
708
Proxies of subinterfaces generated with proxyForInterface should allow
709
access to attributes of both the child and the base interfaces.
711
proxyClass = proxyForInterface(IProxiedSubInterface)
713
proxy = proxyClass(booable)
716
self.failUnless(booable.yayed)
717
self.failUnless(booable.booed)
720
def test_attributeCustomization(self):
722
The original attribute name can be customized via the
723
C{originalAttribute} argument of L{proxyForInterface}: the attribute
724
should change, but the methods of the original object should still be
725
callable, and the attributes still accessible.
728
yayable.ifaceAttribute = object()
729
proxy = proxyForInterface(
730
IProxiedInterface, originalAttribute='foo')(yayable)
731
self.assertIdentical(proxy.foo, yayable)
734
self.assertEquals(proxy.yay(), 1)
735
self.assertIdentical(proxy.ifaceAttribute, yayable.ifaceAttribute)
737
proxy.ifaceAttribute = thingy
738
self.assertIdentical(yayable.ifaceAttribute, thingy)
739
del proxy.ifaceAttribute
740
self.assertFalse(hasattr(yayable, 'ifaceAttribute'))