test_components.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Test cases for Twisted component architecture.
  5. """
  6. from __future__ import division, absolute_import
  7. from functools import wraps
  8. from zope.interface import Interface, implementer, Attribute
  9. from zope.interface.adapter import AdapterRegistry
  10. from twisted.python.compat import comparable, cmp
  11. from twisted.trial import unittest
  12. from twisted.python import components
  13. from twisted.python.components import _addHook, _removeHook, proxyForInterface
  14. class Compo(components.Componentized):
  15. num = 0
  16. def inc(self):
  17. self.num = self.num + 1
  18. return self.num
  19. class IAdept(Interface):
  20. def adaptorFunc():
  21. raise NotImplementedError()
  22. class IElapsed(Interface):
  23. def elapsedFunc():
  24. """
  25. 1!
  26. """
  27. @implementer(IAdept)
  28. class Adept(components.Adapter):
  29. def __init__(self, orig):
  30. self.original = orig
  31. self.num = 0
  32. def adaptorFunc(self):
  33. self.num = self.num + 1
  34. return self.num, self.original.inc()
  35. @implementer(IElapsed)
  36. class Elapsed(components.Adapter):
  37. def elapsedFunc(self):
  38. return 1
  39. class AComp(components.Componentized):
  40. pass
  41. class BComp(AComp):
  42. pass
  43. class CComp(BComp):
  44. pass
  45. class ITest(Interface):
  46. pass
  47. class ITest2(Interface):
  48. pass
  49. class ITest3(Interface):
  50. pass
  51. class ITest4(Interface):
  52. pass
  53. @implementer(ITest, ITest3, ITest4)
  54. class Test(components.Adapter):
  55. def __init__(self, orig):
  56. pass
  57. @implementer(ITest2)
  58. class Test2(object):
  59. temporaryAdapter = 1
  60. def __init__(self, orig):
  61. pass
  62. class RegistryUsingMixin(object):
  63. """
  64. Mixin for test cases which modify the global registry somehow.
  65. """
  66. def setUp(self):
  67. """
  68. Configure L{twisted.python.components.registerAdapter} to mutate an
  69. alternate registry to improve test isolation.
  70. """
  71. # Create a brand new, empty registry and put it onto the components
  72. # module where registerAdapter will use it. Also ensure that it goes
  73. # away at the end of the test.
  74. scratchRegistry = AdapterRegistry()
  75. self.patch(components, 'globalRegistry', scratchRegistry)
  76. # Hook the new registry up to the adapter lookup system and ensure that
  77. # association is also discarded after the test.
  78. hook = _addHook(scratchRegistry)
  79. self.addCleanup(_removeHook, hook)
  80. class ComponentizedTests(unittest.SynchronousTestCase, RegistryUsingMixin):
  81. """
  82. Simple test case for caching in Componentized.
  83. """
  84. def setUp(self):
  85. RegistryUsingMixin.setUp(self)
  86. components.registerAdapter(Test, AComp, ITest)
  87. components.registerAdapter(Test, AComp, ITest3)
  88. components.registerAdapter(Test2, AComp, ITest2)
  89. def testComponentized(self):
  90. components.registerAdapter(Adept, Compo, IAdept)
  91. components.registerAdapter(Elapsed, Compo, IElapsed)
  92. c = Compo()
  93. assert c.getComponent(IAdept).adaptorFunc() == (1, 1)
  94. assert c.getComponent(IAdept).adaptorFunc() == (2, 2)
  95. assert IElapsed(IAdept(c)).elapsedFunc() == 1
  96. def testInheritanceAdaptation(self):
  97. c = CComp()
  98. co1 = c.getComponent(ITest)
  99. co2 = c.getComponent(ITest)
  100. co3 = c.getComponent(ITest2)
  101. co4 = c.getComponent(ITest2)
  102. assert co1 is co2
  103. assert co3 is not co4
  104. c.removeComponent(co1)
  105. co5 = c.getComponent(ITest)
  106. co6 = c.getComponent(ITest)
  107. assert co5 is co6
  108. assert co1 is not co5
  109. def testMultiAdapter(self):
  110. c = CComp()
  111. co1 = c.getComponent(ITest)
  112. co3 = c.getComponent(ITest3)
  113. co4 = c.getComponent(ITest4)
  114. self.assertIsNone(co4)
  115. self.assertIs(co1, co3)
  116. def test_getComponentDefaults(self):
  117. """
  118. Test that a default value specified to Componentized.getComponent if
  119. there is no component for the requested interface.
  120. """
  121. componentized = components.Componentized()
  122. default = object()
  123. self.assertIs(
  124. componentized.getComponent(ITest, default),
  125. default)
  126. self.assertIs(
  127. componentized.getComponent(ITest, default=default),
  128. default)
  129. self.assertIs(
  130. componentized.getComponent(ITest),
  131. None)
  132. def test_setAdapter(self):
  133. """
  134. C{Componentized.setAdapter} sets a component for an interface by
  135. wrapping the instance with the given adapter class.
  136. """
  137. componentized = components.Componentized()
  138. componentized.setAdapter(IAdept, Adept)
  139. component = componentized.getComponent(IAdept)
  140. self.assertEqual(component.original, componentized)
  141. self.assertIsInstance(component, Adept)
  142. def test_addAdapter(self):
  143. """
  144. C{Componentized.setAdapter} adapts the instance by wrapping it with
  145. given adapter class, then stores it using C{addComponent}.
  146. """
  147. componentized = components.Componentized()
  148. componentized.addAdapter(Adept, ignoreClass=True)
  149. component = componentized.getComponent(IAdept)
  150. self.assertEqual(component.original, componentized)
  151. self.assertIsInstance(component, Adept)
  152. def test_setComponent(self):
  153. """
  154. C{Componentized.setComponent} stores the given component using the
  155. given interface as the key.
  156. """
  157. componentized = components.Componentized()
  158. obj = object()
  159. componentized.setComponent(ITest, obj)
  160. self.assertIs(componentized.getComponent(ITest), obj)
  161. def test_unsetComponent(self):
  162. """
  163. C{Componentized.setComponent} removes the cached component for the
  164. given interface.
  165. """
  166. componentized = components.Componentized()
  167. obj = object()
  168. componentized.setComponent(ITest, obj)
  169. componentized.unsetComponent(ITest)
  170. self.assertIsNone(componentized.getComponent(ITest))
  171. def test_reprableComponentized(self):
  172. """
  173. C{ReprableComponentized} has a C{__repr__} that lists its cache.
  174. """
  175. rc = components.ReprableComponentized()
  176. rc.setComponent(ITest, "hello")
  177. result = repr(rc)
  178. self.assertIn("ITest", result)
  179. self.assertIn("hello", result)
  180. class AdapterTests(unittest.SynchronousTestCase):
  181. """Test adapters."""
  182. def testAdapterGetComponent(self):
  183. o = object()
  184. a = Adept(o)
  185. self.assertRaises(components.CannotAdapt, ITest, a)
  186. self.assertIsNone(ITest(a, None))
  187. class IMeta(Interface):
  188. pass
  189. @implementer(IMeta)
  190. class MetaAdder(components.Adapter):
  191. def add(self, num):
  192. return self.original.num + num
  193. @implementer(IMeta)
  194. class BackwardsAdder(components.Adapter):
  195. def add(self, num):
  196. return self.original.num - num
  197. class MetaNumber(object):
  198. """
  199. Integer wrapper for Interface adaptation tests.
  200. """
  201. def __init__(self, num):
  202. self.num = num
  203. class ComponentNumber(components.Componentized):
  204. def __init__(self):
  205. self.num = 0
  206. components.Componentized.__init__(self)
  207. @implementer(IMeta)
  208. class ComponentAdder(components.Adapter):
  209. """
  210. Adder for componentized adapter tests.
  211. """
  212. def __init__(self, original):
  213. components.Adapter.__init__(self, original)
  214. self.num = self.original.num
  215. def add(self, num):
  216. self.num += num
  217. return self.num
  218. class IAttrX(Interface):
  219. """
  220. Base interface for test of adapter with C{__cmp__}.
  221. """
  222. def x():
  223. """
  224. Return a value.
  225. """
  226. class IAttrXX(Interface):
  227. """
  228. Adapted interface for test of adapter with C{__cmp__}.
  229. """
  230. def xx():
  231. """
  232. Return a tuple of values.
  233. """
  234. @implementer(IAttrX)
  235. class Xcellent(object):
  236. """
  237. L{IAttrX} implementation for test of adapter with C{__cmp__}.
  238. """
  239. def x(self):
  240. """
  241. Return a value.
  242. @return: a value
  243. """
  244. return 'x!'
  245. @comparable
  246. class DoubleXAdapter(object):
  247. """
  248. Adapter with __cmp__.
  249. """
  250. num = 42
  251. def __init__(self, original):
  252. self.original = original
  253. def xx(self):
  254. return (self.original.x(), self.original.x())
  255. def __cmp__(self, other):
  256. return cmp(self.num, other.num)
  257. class MetaInterfaceTests(RegistryUsingMixin, unittest.SynchronousTestCase):
  258. def test_basic(self):
  259. """
  260. Registered adapters can be used to adapt classes to an interface.
  261. """
  262. components.registerAdapter(MetaAdder, MetaNumber, IMeta)
  263. n = MetaNumber(1)
  264. self.assertEqual(IMeta(n).add(1), 2)
  265. def testComponentizedInteraction(self):
  266. components.registerAdapter(ComponentAdder, ComponentNumber, IMeta)
  267. c = ComponentNumber()
  268. IMeta(c).add(1)
  269. IMeta(c).add(1)
  270. self.assertEqual(IMeta(c).add(1), 3)
  271. def testAdapterWithCmp(self):
  272. # Make sure that a __cmp__ on an adapter doesn't break anything
  273. components.registerAdapter(DoubleXAdapter, IAttrX, IAttrXX)
  274. xx = IAttrXX(Xcellent())
  275. self.assertEqual(('x!', 'x!'), xx.xx())
  276. class RegistrationTests(RegistryUsingMixin, unittest.SynchronousTestCase):
  277. """
  278. Tests for adapter registration.
  279. """
  280. def _registerAdapterForClassOrInterface(self, original):
  281. """
  282. Register an adapter with L{components.registerAdapter} for the given
  283. class or interface and verify that the adapter can be looked up with
  284. L{components.getAdapterFactory}.
  285. """
  286. adapter = lambda o: None
  287. components.registerAdapter(adapter, original, ITest)
  288. self.assertIs(
  289. components.getAdapterFactory(original, ITest, None),
  290. adapter)
  291. def test_registerAdapterForClass(self):
  292. """
  293. Test that an adapter from a class can be registered and then looked
  294. up.
  295. """
  296. class TheOriginal(object):
  297. pass
  298. return self._registerAdapterForClassOrInterface(TheOriginal)
  299. def test_registerAdapterForInterface(self):
  300. """
  301. Test that an adapter from an interface can be registered and then
  302. looked up.
  303. """
  304. return self._registerAdapterForClassOrInterface(ITest2)
  305. def _duplicateAdapterForClassOrInterface(self, original):
  306. """
  307. Verify that L{components.registerAdapter} raises L{ValueError} if the
  308. from-type/interface and to-interface pair is not unique.
  309. """
  310. firstAdapter = lambda o: False
  311. secondAdapter = lambda o: True
  312. components.registerAdapter(firstAdapter, original, ITest)
  313. self.assertRaises(
  314. ValueError,
  315. components.registerAdapter,
  316. secondAdapter, original, ITest)
  317. # Make sure that the original adapter is still around as well
  318. self.assertIs(
  319. components.getAdapterFactory(original, ITest, None),
  320. firstAdapter)
  321. def test_duplicateAdapterForClass(self):
  322. """
  323. Test that attempting to register a second adapter from a class
  324. raises the appropriate exception.
  325. """
  326. class TheOriginal(object):
  327. pass
  328. return self._duplicateAdapterForClassOrInterface(TheOriginal)
  329. def test_duplicateAdapterForInterface(self):
  330. """
  331. Test that attempting to register a second adapter from an interface
  332. raises the appropriate exception.
  333. """
  334. return self._duplicateAdapterForClassOrInterface(ITest2)
  335. def _duplicateAdapterForClassOrInterfaceAllowed(self, original):
  336. """
  337. Verify that when C{components.ALLOW_DUPLICATES} is set to C{True}, new
  338. adapter registrations for a particular from-type/interface and
  339. to-interface pair replace older registrations.
  340. """
  341. firstAdapter = lambda o: False
  342. secondAdapter = lambda o: True
  343. class TheInterface(Interface):
  344. pass
  345. components.registerAdapter(firstAdapter, original, TheInterface)
  346. components.ALLOW_DUPLICATES = True
  347. try:
  348. components.registerAdapter(secondAdapter, original, TheInterface)
  349. self.assertIs(
  350. components.getAdapterFactory(original, TheInterface, None),
  351. secondAdapter)
  352. finally:
  353. components.ALLOW_DUPLICATES = False
  354. # It should be rejected again at this point
  355. self.assertRaises(
  356. ValueError,
  357. components.registerAdapter,
  358. firstAdapter, original, TheInterface)
  359. self.assertIs(
  360. components.getAdapterFactory(original, TheInterface, None),
  361. secondAdapter)
  362. def test_duplicateAdapterForClassAllowed(self):
  363. """
  364. Test that when L{components.ALLOW_DUPLICATES} is set to a true
  365. value, duplicate registrations from classes are allowed to override
  366. the original registration.
  367. """
  368. class TheOriginal(object):
  369. pass
  370. return self._duplicateAdapterForClassOrInterfaceAllowed(TheOriginal)
  371. def test_duplicateAdapterForInterfaceAllowed(self):
  372. """
  373. Test that when L{components.ALLOW_DUPLICATES} is set to a true
  374. value, duplicate registrations from interfaces are allowed to
  375. override the original registration.
  376. """
  377. class TheOriginal(Interface):
  378. pass
  379. return self._duplicateAdapterForClassOrInterfaceAllowed(TheOriginal)
  380. def _multipleInterfacesForClassOrInterface(self, original):
  381. """
  382. Verify that an adapter can be registered for multiple to-interfaces at a
  383. time.
  384. """
  385. adapter = lambda o: None
  386. components.registerAdapter(adapter, original, ITest, ITest2)
  387. self.assertIs(
  388. components.getAdapterFactory(original, ITest, None), adapter)
  389. self.assertIs(
  390. components.getAdapterFactory(original, ITest2, None), adapter)
  391. def test_multipleInterfacesForClass(self):
  392. """
  393. Test the registration of an adapter from a class to several
  394. interfaces at once.
  395. """
  396. class TheOriginal(object):
  397. pass
  398. return self._multipleInterfacesForClassOrInterface(TheOriginal)
  399. def test_multipleInterfacesForInterface(self):
  400. """
  401. Test the registration of an adapter from an interface to several
  402. interfaces at once.
  403. """
  404. return self._multipleInterfacesForClassOrInterface(ITest3)
  405. def _subclassAdapterRegistrationForClassOrInterface(self, original):
  406. """
  407. Verify that a new adapter can be registered for a particular
  408. to-interface from a subclass of a type or interface which already has an
  409. adapter registered to that interface and that the subclass adapter takes
  410. precedence over the base class adapter.
  411. """
  412. firstAdapter = lambda o: True
  413. secondAdapter = lambda o: False
  414. class TheSubclass(original):
  415. pass
  416. components.registerAdapter(firstAdapter, original, ITest)
  417. components.registerAdapter(secondAdapter, TheSubclass, ITest)
  418. self.assertIs(
  419. components.getAdapterFactory(original, ITest, None),
  420. firstAdapter)
  421. self.assertIs(
  422. components.getAdapterFactory(TheSubclass, ITest, None),
  423. secondAdapter)
  424. def test_subclassAdapterRegistrationForClass(self):
  425. """
  426. Test that an adapter to a particular interface can be registered
  427. from both a class and its subclass.
  428. """
  429. class TheOriginal(object):
  430. pass
  431. return self._subclassAdapterRegistrationForClassOrInterface(TheOriginal)
  432. def test_subclassAdapterRegistrationForInterface(self):
  433. """
  434. Test that an adapter to a particular interface can be registered
  435. from both an interface and its subclass.
  436. """
  437. return self._subclassAdapterRegistrationForClassOrInterface(ITest2)
  438. class IProxiedInterface(Interface):
  439. """
  440. An interface class for use by L{proxyForInterface}.
  441. """
  442. ifaceAttribute = Attribute("""
  443. An example declared attribute, which should be proxied.""")
  444. def yay(*a, **kw):
  445. """
  446. A sample method which should be proxied.
  447. """
  448. class IProxiedSubInterface(IProxiedInterface):
  449. """
  450. An interface that derives from another for use with L{proxyForInterface}.
  451. """
  452. def boo(self):
  453. """
  454. A different sample method which should be proxied.
  455. """
  456. @implementer(IProxiedInterface)
  457. class Yayable(object):
  458. """
  459. A provider of L{IProxiedInterface} which increments a counter for
  460. every call to C{yay}.
  461. @ivar yays: The number of times C{yay} has been called.
  462. """
  463. def __init__(self):
  464. self.yays = 0
  465. self.yayArgs = []
  466. def yay(self, *a, **kw):
  467. """
  468. Increment C{self.yays}.
  469. """
  470. self.yays += 1
  471. self.yayArgs.append((a, kw))
  472. return self.yays
  473. @implementer(IProxiedSubInterface)
  474. class Booable(object):
  475. """
  476. An implementation of IProxiedSubInterface
  477. """
  478. yayed = False
  479. booed = False
  480. def yay(self):
  481. """
  482. Mark the fact that 'yay' has been called.
  483. """
  484. self.yayed = True
  485. def boo(self):
  486. """
  487. Mark the fact that 'boo' has been called.1
  488. """
  489. self.booed = True
  490. class IMultipleMethods(Interface):
  491. """
  492. An interface with multiple methods.
  493. """
  494. def methodOne():
  495. """
  496. The first method. Should return 1.
  497. """
  498. def methodTwo():
  499. """
  500. The second method. Should return 2.
  501. """
  502. class MultipleMethodImplementor(object):
  503. """
  504. A precise implementation of L{IMultipleMethods}.
  505. """
  506. def methodOne(self):
  507. """
  508. @return: 1
  509. """
  510. return 1
  511. def methodTwo(self):
  512. """
  513. @return: 2
  514. """
  515. return 2
  516. class ProxyForInterfaceTests(unittest.SynchronousTestCase):
  517. """
  518. Tests for L{proxyForInterface}.
  519. """
  520. def test_original(self):
  521. """
  522. Proxy objects should have an C{original} attribute which refers to the
  523. original object passed to the constructor.
  524. """
  525. original = object()
  526. proxy = proxyForInterface(IProxiedInterface)(original)
  527. self.assertIs(proxy.original, original)
  528. def test_proxyMethod(self):
  529. """
  530. The class created from L{proxyForInterface} passes methods on an
  531. interface to the object which is passed to its constructor.
  532. """
  533. klass = proxyForInterface(IProxiedInterface)
  534. yayable = Yayable()
  535. proxy = klass(yayable)
  536. proxy.yay()
  537. self.assertEqual(proxy.yay(), 2)
  538. self.assertEqual(yayable.yays, 2)
  539. def test_decoratedProxyMethod(self):
  540. """
  541. Methods of the class created from L{proxyForInterface} can be used with
  542. the decorator-helper L{functools.wraps}.
  543. """
  544. base = proxyForInterface(IProxiedInterface)
  545. class klass(base):
  546. @wraps(base.yay)
  547. def yay(self):
  548. self.original.yays += 1
  549. return base.yay(self)
  550. original = Yayable()
  551. yayable = klass(original)
  552. yayable.yay()
  553. self.assertEqual(2, original.yays)
  554. def test_proxyAttribute(self):
  555. """
  556. Proxy objects should proxy declared attributes, but not other
  557. attributes.
  558. """
  559. yayable = Yayable()
  560. yayable.ifaceAttribute = object()
  561. proxy = proxyForInterface(IProxiedInterface)(yayable)
  562. self.assertIs(proxy.ifaceAttribute, yayable.ifaceAttribute)
  563. self.assertRaises(AttributeError, lambda: proxy.yays)
  564. def test_proxySetAttribute(self):
  565. """
  566. The attributes that proxy objects proxy should be assignable and affect
  567. the original object.
  568. """
  569. yayable = Yayable()
  570. proxy = proxyForInterface(IProxiedInterface)(yayable)
  571. thingy = object()
  572. proxy.ifaceAttribute = thingy
  573. self.assertIs(yayable.ifaceAttribute, thingy)
  574. def test_proxyDeleteAttribute(self):
  575. """
  576. The attributes that proxy objects proxy should be deletable and affect
  577. the original object.
  578. """
  579. yayable = Yayable()
  580. yayable.ifaceAttribute = None
  581. proxy = proxyForInterface(IProxiedInterface)(yayable)
  582. del proxy.ifaceAttribute
  583. self.assertFalse(hasattr(yayable, 'ifaceAttribute'))
  584. def test_multipleMethods(self):
  585. """
  586. [Regression test] The proxy should send its method calls to the correct
  587. method, not the incorrect one.
  588. """
  589. multi = MultipleMethodImplementor()
  590. proxy = proxyForInterface(IMultipleMethods)(multi)
  591. self.assertEqual(proxy.methodOne(), 1)
  592. self.assertEqual(proxy.methodTwo(), 2)
  593. def test_subclassing(self):
  594. """
  595. It is possible to subclass the result of L{proxyForInterface}.
  596. """
  597. class SpecializedProxy(proxyForInterface(IProxiedInterface)):
  598. """
  599. A specialized proxy which can decrement the number of yays.
  600. """
  601. def boo(self):
  602. """
  603. Decrement the number of yays.
  604. """
  605. self.original.yays -= 1
  606. yayable = Yayable()
  607. special = SpecializedProxy(yayable)
  608. self.assertEqual(yayable.yays, 0)
  609. special.boo()
  610. self.assertEqual(yayable.yays, -1)
  611. def test_proxyName(self):
  612. """
  613. The name of a proxy class indicates which interface it proxies.
  614. """
  615. proxy = proxyForInterface(IProxiedInterface)
  616. self.assertEqual(
  617. proxy.__name__,
  618. "(Proxy for "
  619. "twisted.python.test.test_components.IProxiedInterface)")
  620. def test_implements(self):
  621. """
  622. The resulting proxy implements the interface that it proxies.
  623. """
  624. proxy = proxyForInterface(IProxiedInterface)
  625. self.assertTrue(IProxiedInterface.implementedBy(proxy))
  626. def test_proxyDescriptorGet(self):
  627. """
  628. _ProxyDescriptor's __get__ method should return the appropriate
  629. attribute of its argument's 'original' attribute if it is invoked with
  630. an object. If it is invoked with None, it should return a false
  631. class-method emulator instead.
  632. For some reason, Python's documentation recommends to define
  633. descriptors' __get__ methods with the 'type' parameter as optional,
  634. despite the fact that Python itself never actually calls the descriptor
  635. that way. This is probably do to support 'foo.__get__(bar)' as an
  636. idiom. Let's make sure that the behavior is correct. Since we don't
  637. actually use the 'type' argument at all, this test calls it the
  638. idiomatic way to ensure that signature works; test_proxyInheritance
  639. verifies the how-Python-actually-calls-it signature.
  640. """
  641. class Sample(object):
  642. called = False
  643. def hello(self):
  644. self.called = True
  645. fakeProxy = Sample()
  646. testObject = Sample()
  647. fakeProxy.original = testObject
  648. pd = components._ProxyDescriptor("hello", "original")
  649. self.assertEqual(pd.__get__(fakeProxy), testObject.hello)
  650. fakeClassMethod = pd.__get__(None)
  651. fakeClassMethod(fakeProxy)
  652. self.assertTrue(testObject.called)
  653. def test_proxyInheritance(self):
  654. """
  655. Subclasses of the class returned from L{proxyForInterface} should be
  656. able to upcall methods by reference to their superclass, as any normal
  657. Python class can.
  658. """
  659. class YayableWrapper(proxyForInterface(IProxiedInterface)):
  660. """
  661. This class does not override any functionality.
  662. """
  663. class EnhancedWrapper(YayableWrapper):
  664. """
  665. This class overrides the 'yay' method.
  666. """
  667. wrappedYays = 1
  668. def yay(self, *a, **k):
  669. self.wrappedYays += 1
  670. return YayableWrapper.yay(self, *a, **k) + 7
  671. yayable = Yayable()
  672. wrapper = EnhancedWrapper(yayable)
  673. self.assertEqual(wrapper.yay(3, 4, x=5, y=6), 8)
  674. self.assertEqual(yayable.yayArgs,
  675. [((3, 4), dict(x=5, y=6))])
  676. def test_interfaceInheritance(self):
  677. """
  678. Proxies of subinterfaces generated with proxyForInterface should allow
  679. access to attributes of both the child and the base interfaces.
  680. """
  681. proxyClass = proxyForInterface(IProxiedSubInterface)
  682. booable = Booable()
  683. proxy = proxyClass(booable)
  684. proxy.yay()
  685. proxy.boo()
  686. self.assertTrue(booable.yayed)
  687. self.assertTrue(booable.booed)
  688. def test_attributeCustomization(self):
  689. """
  690. The original attribute name can be customized via the
  691. C{originalAttribute} argument of L{proxyForInterface}: the attribute
  692. should change, but the methods of the original object should still be
  693. callable, and the attributes still accessible.
  694. """
  695. yayable = Yayable()
  696. yayable.ifaceAttribute = object()
  697. proxy = proxyForInterface(
  698. IProxiedInterface, originalAttribute='foo')(yayable)
  699. self.assertIs(proxy.foo, yayable)
  700. # Check the behavior
  701. self.assertEqual(proxy.yay(), 1)
  702. self.assertIs(proxy.ifaceAttribute, yayable.ifaceAttribute)
  703. thingy = object()
  704. proxy.ifaceAttribute = thingy
  705. self.assertIs(yayable.ifaceAttribute, thingy)
  706. del proxy.ifaceAttribute
  707. self.assertFalse(hasattr(yayable, 'ifaceAttribute'))