OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 | |
5 """ | |
6 Test cases for Twisted component architecture. | |
7 """ | |
8 | |
9 from zope.interface import Interface, implements, Attribute | |
10 | |
11 from twisted.trial import unittest | |
12 from twisted.python import components | |
13 from twisted.python.components import proxyForInterface | |
14 | |
15 | |
16 class InterfacesTestCase(unittest.TestCase): | |
17 """Test interfaces.""" | |
18 | |
19 class Compo(components.Componentized): | |
20 num = 0 | |
21 def inc(self): | |
22 self.num = self.num + 1 | |
23 return self.num | |
24 | |
25 class IAdept(Interface): | |
26 def adaptorFunc(): | |
27 raise NotImplementedError() | |
28 | |
29 class IElapsed(Interface): | |
30 def elapsedFunc(): | |
31 """ | |
32 1! | |
33 """ | |
34 | |
35 class Adept(components.Adapter): | |
36 implements(IAdept) | |
37 def __init__(self, orig): | |
38 self.original = orig | |
39 self.num = 0 | |
40 def adaptorFunc(self): | |
41 self.num = self.num + 1 | |
42 return self.num, self.original.inc() | |
43 | |
44 class Elapsed(components.Adapter): | |
45 implements(IElapsed) | |
46 def elapsedFunc(self): | |
47 return 1 | |
48 | |
49 components.registerAdapter(Adept, Compo, IAdept) | |
50 components.registerAdapter(Elapsed, Compo, IElapsed) | |
51 | |
52 class AComp(components.Componentized): | |
53 pass | |
54 class BComp(AComp): | |
55 pass | |
56 class CComp(BComp): | |
57 pass | |
58 | |
59 class ITest(Interface): | |
60 pass | |
61 class ITest2(Interface): | |
62 pass | |
63 class ITest3(Interface): | |
64 pass | |
65 class ITest4(Interface): | |
66 pass | |
67 class Test(components.Adapter): | |
68 implements(ITest, ITest3, ITest4) | |
69 def __init__(self, orig): | |
70 pass | |
71 class Test2: | |
72 implements(ITest2) | |
73 temporaryAdapter = 1 | |
74 def __init__(self, orig): | |
75 pass | |
76 | |
77 components.registerAdapter(Test, AComp, ITest) | |
78 components.registerAdapter(Test, AComp, ITest3) | |
79 components.registerAdapter(Test2, AComp, ITest2) | |
80 | |
81 | |
82 | |
83 | |
84 class ComponentizedTestCase(unittest.TestCase): | |
85 """Simple test case for caching in Componentized. | |
86 """ | |
87 def testComponentized(self): | |
88 c = Compo() | |
89 assert c.getComponent(IAdept).adaptorFunc() == (1, 1) | |
90 assert c.getComponent(IAdept).adaptorFunc() == (2, 2) | |
91 assert IElapsed(IAdept(c)).elapsedFunc() == 1 | |
92 | |
93 def testInheritanceAdaptation(self): | |
94 c = CComp() | |
95 co1 = c.getComponent(ITest) | |
96 co2 = c.getComponent(ITest) | |
97 co3 = c.getComponent(ITest2) | |
98 co4 = c.getComponent(ITest2) | |
99 assert co1 is co2 | |
100 assert co3 is not co4 | |
101 c.removeComponent(co1) | |
102 co5 = c.getComponent(ITest) | |
103 co6 = c.getComponent(ITest) | |
104 assert co5 is co6 | |
105 assert co1 is not co5 | |
106 | |
107 def testMultiAdapter(self): | |
108 c = CComp() | |
109 co1 = c.getComponent(ITest) | |
110 co2 = c.getComponent(ITest2) | |
111 co3 = c.getComponent(ITest3) | |
112 co4 = c.getComponent(ITest4) | |
113 assert co4 == None | |
114 assert co1 is co3 | |
115 | |
116 | |
117 def test_getComponentDefaults(self): | |
118 """ | |
119 Test that a default value specified to Componentized.getComponent if | |
120 there is no component for the requested interface. | |
121 """ | |
122 componentized = components.Componentized() | |
123 default = object() | |
124 self.assertIdentical( | |
125 componentized.getComponent(ITest, default), | |
126 default) | |
127 self.assertIdentical( | |
128 componentized.getComponent(ITest, default=default), | |
129 default) | |
130 self.assertIdentical( | |
131 componentized.getComponent(ITest), | |
132 None) | |
133 | |
134 | |
135 | |
136 class AdapterTestCase(unittest.TestCase): | |
137 """Test adapters.""" | |
138 | |
139 def testAdapterGetComponent(self): | |
140 o = object() | |
141 a = Adept(o) | |
142 self.assertRaises(components.CannotAdapt, ITest, a) | |
143 self.assertEquals(ITest(a, None), None) | |
144 | |
145 | |
146 | |
147 class IMeta(Interface): | |
148 pass | |
149 | |
150 class MetaAdder(components.Adapter): | |
151 implements(IMeta) | |
152 def add(self, num): | |
153 return self.original.num + num | |
154 | |
155 class BackwardsAdder(components.Adapter): | |
156 implements(IMeta) | |
157 def add(self, num): | |
158 return self.original.num - num | |
159 | |
160 class MetaNumber: | |
161 def __init__(self, num): | |
162 self.num = num | |
163 | |
164 class FakeAdder: | |
165 def add(self, num): | |
166 return num + 5 | |
167 | |
168 class FakeNumber: | |
169 num = 3 | |
170 | |
171 class ComponentNumber(components.Componentized): | |
172 def __init__(self): | |
173 self.num = 0 | |
174 components.Componentized.__init__(self) | |
175 | |
176 class ComponentMeta(components.Adapter): | |
177 implements(IMeta) | |
178 def __init__(self, original): | |
179 components.Adapter.__init__(self, original) | |
180 self.num = self.original.num | |
181 | |
182 class ComponentAdder(ComponentMeta): | |
183 def add(self, num): | |
184 self.num += num | |
185 return self.num | |
186 | |
187 class ComponentDoubler(ComponentMeta): | |
188 def add(self, num): | |
189 self.num += (num * 2) | |
190 return self.original.num | |
191 | |
192 components.registerAdapter(MetaAdder, MetaNumber, IMeta) | |
193 components.registerAdapter(ComponentAdder, ComponentNumber, IMeta) | |
194 | |
195 class IAttrX(Interface): | |
196 def x(): | |
197 pass | |
198 | |
199 class IAttrXX(Interface): | |
200 def xx(): | |
201 pass | |
202 | |
203 class Xcellent: | |
204 implements(IAttrX) | |
205 def x(self): | |
206 return 'x!' | |
207 | |
208 class DoubleXAdapter: | |
209 num = 42 | |
210 def __init__(self, original): | |
211 self.original = original | |
212 def xx(self): | |
213 return (self.original.x(), self.original.x()) | |
214 def __cmp__(self, other): | |
215 return cmp(self.num, other.num) | |
216 | |
217 components.registerAdapter(DoubleXAdapter, IAttrX, IAttrXX) | |
218 | |
219 class TestMetaInterface(unittest.TestCase): | |
220 | |
221 def testBasic(self): | |
222 n = MetaNumber(1) | |
223 self.assertEquals(IMeta(n).add(1), 2) | |
224 | |
225 def testComponentizedInteraction(self): | |
226 c = ComponentNumber() | |
227 IMeta(c).add(1) | |
228 IMeta(c).add(1) | |
229 self.assertEquals(IMeta(c).add(1), 3) | |
230 | |
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()) | |
235 | |
236 | |
237 class RegistrationTestCase(unittest.TestCase): | |
238 """ | |
239 Tests for adapter registration. | |
240 """ | |
241 def _registerAdapterForClassOrInterface(self, original): | |
242 adapter = lambda o: None | |
243 class TheInterface(Interface): | |
244 pass | |
245 components.registerAdapter(adapter, original, TheInterface) | |
246 self.assertIdentical( | |
247 components.getAdapterFactory(original, TheInterface, None), | |
248 adapter) | |
249 | |
250 | |
251 def test_registerAdapterForClass(self): | |
252 """ | |
253 Test that an adapter from a class can be registered and then looked | |
254 up. | |
255 """ | |
256 class TheOriginal(object): | |
257 pass | |
258 return self._registerAdapterForClassOrInterface(TheOriginal) | |
259 | |
260 | |
261 def test_registerAdapterForInterface(self): | |
262 """ | |
263 Test that an adapter from an interface can be registered and then | |
264 looked up. | |
265 """ | |
266 class TheOriginal(Interface): | |
267 pass | |
268 return self._registerAdapterForClassOrInterface(TheOriginal) | |
269 | |
270 | |
271 def _duplicateAdapterForClassOrInterface(self, original): | |
272 firstAdapter = lambda o: False | |
273 secondAdapter = lambda o: True | |
274 class TheInterface(Interface): | |
275 pass | |
276 components.registerAdapter(firstAdapter, original, TheInterface) | |
277 self.assertRaises( | |
278 ValueError, | |
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), | |
284 firstAdapter) | |
285 | |
286 | |
287 def test_duplicateAdapterForClass(self): | |
288 """ | |
289 Test that attempting to register a second adapter from a class | |
290 raises the appropriate exception. | |
291 """ | |
292 class TheOriginal(object): | |
293 pass | |
294 return self._duplicateAdapterForClassOrInterface(TheOriginal) | |
295 | |
296 | |
297 def test_duplicateAdapterForInterface(self): | |
298 """ | |
299 Test that attempting to register a second adapter from an interface | |
300 raises the appropriate exception. | |
301 """ | |
302 class TheOriginal(Interface): | |
303 pass | |
304 return self._duplicateAdapterForClassOrInterface(TheOriginal) | |
305 | |
306 | |
307 def _duplicateAdapterForClassOrInterfaceAllowed(self, original): | |
308 firstAdapter = lambda o: False | |
309 secondAdapter = lambda o: True | |
310 class TheInterface(Interface): | |
311 pass | |
312 components.registerAdapter(firstAdapter, original, TheInterface) | |
313 components.ALLOW_DUPLICATES = True | |
314 try: | |
315 components.registerAdapter(secondAdapter, original, TheInterface) | |
316 self.assertIdentical( | |
317 components.getAdapterFactory(original, TheInterface, None), | |
318 secondAdapter) | |
319 finally: | |
320 components.ALLOW_DUPLICATES = False | |
321 | |
322 # It should be rejected again at this point | |
323 self.assertRaises( | |
324 ValueError, | |
325 components.registerAdapter, | |
326 firstAdapter, original, TheInterface) | |
327 | |
328 self.assertIdentical( | |
329 components.getAdapterFactory(original, TheInterface, None), | |
330 secondAdapter) | |
331 | |
332 def test_duplicateAdapterForClassAllowed(self): | |
333 """ | |
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. | |
337 """ | |
338 class TheOriginal(object): | |
339 pass | |
340 return self._duplicateAdapterForClassOrInterfaceAllowed(TheOriginal) | |
341 | |
342 | |
343 def test_duplicateAdapterForInterfaceAllowed(self): | |
344 """ | |
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. | |
348 """ | |
349 class TheOriginal(Interface): | |
350 pass | |
351 return self._duplicateAdapterForClassOrInterfaceAllowed(TheOriginal) | |
352 | |
353 | |
354 def _multipleInterfacesForClassOrInterface(self, original): | |
355 adapter = lambda o: None | |
356 class FirstInterface(Interface): | |
357 pass | |
358 class SecondInterface(Interface): | |
359 pass | |
360 components.registerAdapter(adapter, original, FirstInterface, SecondInte
rface) | |
361 self.assertIdentical( | |
362 components.getAdapterFactory(original, FirstInterface, None), | |
363 adapter) | |
364 self.assertIdentical( | |
365 components.getAdapterFactory(original, SecondInterface, None), | |
366 adapter) | |
367 | |
368 | |
369 def test_multipleInterfacesForClass(self): | |
370 """ | |
371 Test the registration of an adapter from a class to several | |
372 interfaces at once. | |
373 """ | |
374 class TheOriginal(object): | |
375 pass | |
376 return self._multipleInterfacesForClassOrInterface(TheOriginal) | |
377 | |
378 | |
379 def test_multipleInterfacesForInterface(self): | |
380 """ | |
381 Test the registration of an adapter from an interface to several | |
382 interfaces at once. | |
383 """ | |
384 class TheOriginal(Interface): | |
385 pass | |
386 return self._multipleInterfacesForClassOrInterface(TheOriginal) | |
387 | |
388 | |
389 def _subclassAdapterRegistrationForClassOrInterface(self, original): | |
390 firstAdapter = lambda o: True | |
391 secondAdapter = lambda o: False | |
392 class TheSubclass(original): | |
393 pass | |
394 class TheInterface(Interface): | |
395 pass | |
396 components.registerAdapter(firstAdapter, original, TheInterface) | |
397 components.registerAdapter(secondAdapter, TheSubclass, TheInterface) | |
398 self.assertIdentical( | |
399 components.getAdapterFactory(original, TheInterface, None), | |
400 firstAdapter) | |
401 self.assertIdentical( | |
402 components.getAdapterFactory(TheSubclass, TheInterface, None), | |
403 secondAdapter) | |
404 | |
405 | |
406 def test_subclassAdapterRegistrationForClass(self): | |
407 """ | |
408 Test that an adapter to a particular interface can be registered | |
409 from both a class and its subclass. | |
410 """ | |
411 class TheOriginal(object): | |
412 pass | |
413 return self._subclassAdapterRegistrationForClassOrInterface(TheOriginal) | |
414 | |
415 | |
416 def test_subclassAdapterRegistrationForInterface(self): | |
417 """ | |
418 Test that an adapter to a particular interface can be registered | |
419 from both an interface and its subclass. | |
420 """ | |
421 class TheOriginal(Interface): | |
422 pass | |
423 return self._subclassAdapterRegistrationForClassOrInterface(TheOriginal) | |
424 | |
425 | |
426 | |
427 class IProxiedInterface(Interface): | |
428 """ | |
429 An interface class for use by L{proxyForInterface}. | |
430 """ | |
431 | |
432 ifaceAttribute = Attribute(""" | |
433 An example declared attribute, which should be proxied.""") | |
434 | |
435 def yay(*a, **kw): | |
436 """ | |
437 A sample method which should be proxied. | |
438 """ | |
439 | |
440 class IProxiedSubInterface(IProxiedInterface): | |
441 """ | |
442 An interface that derives from another for use with L{proxyForInterface}. | |
443 """ | |
444 | |
445 def boo(self): | |
446 """ | |
447 A different sample method which should be proxied. | |
448 """ | |
449 | |
450 | |
451 | |
452 class Yayable(object): | |
453 """ | |
454 A provider of L{IProxiedInterface} which increments a counter for | |
455 every call to C{yay}. | |
456 | |
457 @ivar yays: The number of times C{yay} has been called. | |
458 """ | |
459 implements(IProxiedInterface) | |
460 | |
461 def __init__(self): | |
462 self.yays = 0 | |
463 self.yayArgs = [] | |
464 | |
465 def yay(self, *a, **kw): | |
466 """ | |
467 Increment C{self.yays}. | |
468 """ | |
469 self.yays += 1 | |
470 self.yayArgs.append((a, kw)) | |
471 return self.yays | |
472 | |
473 | |
474 class Booable(object): | |
475 """ | |
476 An implementation of IProxiedSubInterface | |
477 """ | |
478 implements(IProxiedSubInterface) | |
479 yayed = False | |
480 booed = False | |
481 def yay(self): | |
482 """ | |
483 Mark the fact that 'yay' has been called. | |
484 """ | |
485 self.yayed = True | |
486 | |
487 | |
488 def boo(self): | |
489 """ | |
490 Mark the fact that 'boo' has been called.1 | |
491 """ | |
492 self.booed = True | |
493 | |
494 | |
495 | |
496 class IMultipleMethods(Interface): | |
497 """ | |
498 An interface with multiple methods. | |
499 """ | |
500 | |
501 def methodOne(): | |
502 """ | |
503 The first method. Should return 1. | |
504 """ | |
505 | |
506 def methodTwo(): | |
507 """ | |
508 The second method. Should return 2. | |
509 """ | |
510 | |
511 | |
512 | |
513 class MultipleMethodImplementor(object): | |
514 """ | |
515 A precise implementation of L{IMultipleMethods}. | |
516 """ | |
517 | |
518 def methodOne(self): | |
519 """ | |
520 @return: 1 | |
521 """ | |
522 return 1 | |
523 | |
524 | |
525 def methodTwo(self): | |
526 """ | |
527 @return: 2 | |
528 """ | |
529 return 2 | |
530 | |
531 | |
532 | |
533 class ProxyForInterfaceTests(unittest.TestCase): | |
534 """ | |
535 Tests for L{proxyForInterface}. | |
536 """ | |
537 | |
538 def test_original(self): | |
539 """ | |
540 Proxy objects should have an C{original} attribute which refers to the | |
541 original object passed to the constructor. | |
542 """ | |
543 original = object() | |
544 proxy = proxyForInterface(IProxiedInterface)(original) | |
545 self.assertIdentical(proxy.original, original) | |
546 | |
547 | |
548 def test_proxyMethod(self): | |
549 """ | |
550 The class created from L{proxyForInterface} passes methods on an | |
551 interface to the object which is passed to its constructor. | |
552 """ | |
553 klass = proxyForInterface(IProxiedInterface) | |
554 yayable = Yayable() | |
555 proxy = klass(yayable) | |
556 proxy.yay() | |
557 self.assertEquals(proxy.yay(), 2) | |
558 self.assertEquals(yayable.yays, 2) | |
559 | |
560 | |
561 def test_proxyAttribute(self): | |
562 """ | |
563 Proxy objects should proxy declared attributes, but not other | |
564 attributes. | |
565 """ | |
566 yayable = Yayable() | |
567 yayable.ifaceAttribute = object() | |
568 proxy = proxyForInterface(IProxiedInterface)(yayable) | |
569 self.assertIdentical(proxy.ifaceAttribute, yayable.ifaceAttribute) | |
570 self.assertRaises(AttributeError, lambda: proxy.yays) | |
571 | |
572 | |
573 def test_proxySetAttribute(self): | |
574 """ | |
575 The attributes that proxy objects proxy should be assignable and affect | |
576 the original object. | |
577 """ | |
578 yayable = Yayable() | |
579 proxy = proxyForInterface(IProxiedInterface)(yayable) | |
580 thingy = object() | |
581 proxy.ifaceAttribute = thingy | |
582 self.assertIdentical(yayable.ifaceAttribute, thingy) | |
583 | |
584 | |
585 def test_proxyDeleteAttribute(self): | |
586 """ | |
587 The attributes that proxy objects proxy should be deletable and affect | |
588 the original object. | |
589 """ | |
590 yayable = Yayable() | |
591 yayable.ifaceAttribute = None | |
592 proxy = proxyForInterface(IProxiedInterface)(yayable) | |
593 del proxy.ifaceAttribute | |
594 self.assertFalse(hasattr(yayable, 'ifaceAttribute')) | |
595 | |
596 | |
597 def test_multipleMethods(self): | |
598 """ | |
599 [Regression test] The proxy should send its method calls to the correct | |
600 method, not the incorrect one. | |
601 """ | |
602 multi = MultipleMethodImplementor() | |
603 proxy = proxyForInterface(IMultipleMethods)(multi) | |
604 self.assertEquals(proxy.methodOne(), 1) | |
605 self.assertEquals(proxy.methodTwo(), 2) | |
606 | |
607 | |
608 def test_subclassing(self): | |
609 """ | |
610 It is possible to subclass the result of L{proxyForInterface}. | |
611 """ | |
612 | |
613 class SpecializedProxy(proxyForInterface(IProxiedInterface)): | |
614 """ | |
615 A specialized proxy which can decrement the number of yays. | |
616 """ | |
617 def boo(self): | |
618 """ | |
619 Decrement the number of yays. | |
620 """ | |
621 self.original.yays -= 1 | |
622 | |
623 yayable = Yayable() | |
624 special = SpecializedProxy(yayable) | |
625 self.assertEquals(yayable.yays, 0) | |
626 special.boo() | |
627 self.assertEquals(yayable.yays, -1) | |
628 | |
629 | |
630 def test_proxyName(self): | |
631 """ | |
632 The name of a proxy class indicates which interface it proxies. | |
633 """ | |
634 proxy = proxyForInterface(IProxiedInterface) | |
635 self.assertEquals( | |
636 proxy.__name__, | |
637 "(Proxy for " | |
638 "twisted.python.test.test_components.IProxiedInterface)") | |
639 | |
640 | |
641 def test_provides(self): | |
642 """ | |
643 The resulting proxy provides the Interface that it proxies. | |
644 """ | |
645 proxy = proxyForInterface(IProxiedInterface) | |
646 self.assertTrue(IProxiedInterface.providedBy(proxy)) | |
647 | |
648 | |
649 def test_proxyDescriptorGet(self): | |
650 """ | |
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. | |
655 | |
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. | |
664 """ | |
665 class Sample: | |
666 called = False | |
667 def hello(self): | |
668 self.called = True | |
669 fakeProxy = Sample() | |
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) | |
677 | |
678 | |
679 def test_proxyInheritance(self): | |
680 """ | |
681 Subclasses of the class returned from L{proxyForInterface} should be | |
682 able to upcall methods by reference to their superclass, as any normal | |
683 Python class can. | |
684 """ | |
685 class YayableWrapper(proxyForInterface(IProxiedInterface)): | |
686 """ | |
687 This class does not override any functionality. | |
688 """ | |
689 | |
690 class EnhancedWrapper(YayableWrapper): | |
691 """ | |
692 This class overrides the 'yay' method. | |
693 """ | |
694 wrappedYays = 1 | |
695 def yay(self, *a, **k): | |
696 self.wrappedYays += 1 | |
697 return YayableWrapper.yay(self, *a, **k) + 7 | |
698 | |
699 yayable = Yayable() | |
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))]) | |
704 | |
705 | |
706 def test_interfaceInheritance(self): | |
707 """ | |
708 Proxies of subinterfaces generated with proxyForInterface should allow | |
709 access to attributes of both the child and the base interfaces. | |
710 """ | |
711 proxyClass = proxyForInterface(IProxiedSubInterface) | |
712 booable = Booable() | |
713 proxy = proxyClass(booable) | |
714 proxy.yay() | |
715 proxy.boo() | |
716 self.failUnless(booable.yayed) | |
717 self.failUnless(booable.booed) | |
718 | |
719 | |
720 def test_attributeCustomization(self): | |
721 """ | |
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. | |
726 """ | |
727 yayable = Yayable() | |
728 yayable.ifaceAttribute = object() | |
729 proxy = proxyForInterface( | |
730 IProxiedInterface, originalAttribute='foo')(yayable) | |
731 self.assertIdentical(proxy.foo, yayable) | |
732 | |
733 # Check the behavior | |
734 self.assertEquals(proxy.yay(), 1) | |
735 self.assertIdentical(proxy.ifaceAttribute, yayable.ifaceAttribute) | |
736 thingy = object() | |
737 proxy.ifaceAttribute = thingy | |
738 self.assertIdentical(yayable.ifaceAttribute, thingy) | |
739 del proxy.ifaceAttribute | |
740 self.assertFalse(hasattr(yayable, 'ifaceAttribute')) | |
741 | |
OLD | NEW |