| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898 |
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- Test cases for Twisted component architecture.
- """
- from __future__ import division, absolute_import
- from functools import wraps
- from zope.interface import Interface, implementer, Attribute
- from zope.interface.adapter import AdapterRegistry
- from twisted.python.compat import comparable, cmp
- from twisted.trial import unittest
- from twisted.python import components
- from twisted.python.components import _addHook, _removeHook, proxyForInterface
- class Compo(components.Componentized):
- num = 0
- def inc(self):
- self.num = self.num + 1
- return self.num
- class IAdept(Interface):
- def adaptorFunc():
- raise NotImplementedError()
- class IElapsed(Interface):
- def elapsedFunc():
- """
- 1!
- """
- @implementer(IAdept)
- class Adept(components.Adapter):
- def __init__(self, orig):
- self.original = orig
- self.num = 0
- def adaptorFunc(self):
- self.num = self.num + 1
- return self.num, self.original.inc()
- @implementer(IElapsed)
- class Elapsed(components.Adapter):
- def elapsedFunc(self):
- return 1
- class AComp(components.Componentized):
- pass
- class BComp(AComp):
- pass
- class CComp(BComp):
- pass
- class ITest(Interface):
- pass
- class ITest2(Interface):
- pass
- class ITest3(Interface):
- pass
- class ITest4(Interface):
- pass
- @implementer(ITest, ITest3, ITest4)
- class Test(components.Adapter):
- def __init__(self, orig):
- pass
- @implementer(ITest2)
- class Test2(object):
- temporaryAdapter = 1
- def __init__(self, orig):
- pass
- class RegistryUsingMixin(object):
- """
- Mixin for test cases which modify the global registry somehow.
- """
- def setUp(self):
- """
- Configure L{twisted.python.components.registerAdapter} to mutate an
- alternate registry to improve test isolation.
- """
- # Create a brand new, empty registry and put it onto the components
- # module where registerAdapter will use it. Also ensure that it goes
- # away at the end of the test.
- scratchRegistry = AdapterRegistry()
- self.patch(components, 'globalRegistry', scratchRegistry)
- # Hook the new registry up to the adapter lookup system and ensure that
- # association is also discarded after the test.
- hook = _addHook(scratchRegistry)
- self.addCleanup(_removeHook, hook)
- class ComponentizedTests(unittest.SynchronousTestCase, RegistryUsingMixin):
- """
- Simple test case for caching in Componentized.
- """
- def setUp(self):
- RegistryUsingMixin.setUp(self)
- components.registerAdapter(Test, AComp, ITest)
- components.registerAdapter(Test, AComp, ITest3)
- components.registerAdapter(Test2, AComp, ITest2)
- def testComponentized(self):
- components.registerAdapter(Adept, Compo, IAdept)
- components.registerAdapter(Elapsed, Compo, IElapsed)
- c = Compo()
- assert c.getComponent(IAdept).adaptorFunc() == (1, 1)
- assert c.getComponent(IAdept).adaptorFunc() == (2, 2)
- assert IElapsed(IAdept(c)).elapsedFunc() == 1
- def testInheritanceAdaptation(self):
- c = CComp()
- co1 = c.getComponent(ITest)
- co2 = c.getComponent(ITest)
- co3 = c.getComponent(ITest2)
- co4 = c.getComponent(ITest2)
- assert co1 is co2
- assert co3 is not co4
- c.removeComponent(co1)
- co5 = c.getComponent(ITest)
- co6 = c.getComponent(ITest)
- assert co5 is co6
- assert co1 is not co5
- def testMultiAdapter(self):
- c = CComp()
- co1 = c.getComponent(ITest)
- co3 = c.getComponent(ITest3)
- co4 = c.getComponent(ITest4)
- self.assertIsNone(co4)
- self.assertIs(co1, co3)
- def test_getComponentDefaults(self):
- """
- Test that a default value specified to Componentized.getComponent if
- there is no component for the requested interface.
- """
- componentized = components.Componentized()
- default = object()
- self.assertIs(
- componentized.getComponent(ITest, default),
- default)
- self.assertIs(
- componentized.getComponent(ITest, default=default),
- default)
- self.assertIs(
- componentized.getComponent(ITest),
- None)
- def test_setAdapter(self):
- """
- C{Componentized.setAdapter} sets a component for an interface by
- wrapping the instance with the given adapter class.
- """
- componentized = components.Componentized()
- componentized.setAdapter(IAdept, Adept)
- component = componentized.getComponent(IAdept)
- self.assertEqual(component.original, componentized)
- self.assertIsInstance(component, Adept)
- def test_addAdapter(self):
- """
- C{Componentized.setAdapter} adapts the instance by wrapping it with
- given adapter class, then stores it using C{addComponent}.
- """
- componentized = components.Componentized()
- componentized.addAdapter(Adept, ignoreClass=True)
- component = componentized.getComponent(IAdept)
- self.assertEqual(component.original, componentized)
- self.assertIsInstance(component, Adept)
- def test_setComponent(self):
- """
- C{Componentized.setComponent} stores the given component using the
- given interface as the key.
- """
- componentized = components.Componentized()
- obj = object()
- componentized.setComponent(ITest, obj)
- self.assertIs(componentized.getComponent(ITest), obj)
- def test_unsetComponent(self):
- """
- C{Componentized.setComponent} removes the cached component for the
- given interface.
- """
- componentized = components.Componentized()
- obj = object()
- componentized.setComponent(ITest, obj)
- componentized.unsetComponent(ITest)
- self.assertIsNone(componentized.getComponent(ITest))
- def test_reprableComponentized(self):
- """
- C{ReprableComponentized} has a C{__repr__} that lists its cache.
- """
- rc = components.ReprableComponentized()
- rc.setComponent(ITest, "hello")
- result = repr(rc)
- self.assertIn("ITest", result)
- self.assertIn("hello", result)
- class AdapterTests(unittest.SynchronousTestCase):
- """Test adapters."""
- def testAdapterGetComponent(self):
- o = object()
- a = Adept(o)
- self.assertRaises(components.CannotAdapt, ITest, a)
- self.assertIsNone(ITest(a, None))
- class IMeta(Interface):
- pass
- @implementer(IMeta)
- class MetaAdder(components.Adapter):
- def add(self, num):
- return self.original.num + num
- @implementer(IMeta)
- class BackwardsAdder(components.Adapter):
- def add(self, num):
- return self.original.num - num
- class MetaNumber(object):
- """
- Integer wrapper for Interface adaptation tests.
- """
- def __init__(self, num):
- self.num = num
- class ComponentNumber(components.Componentized):
- def __init__(self):
- self.num = 0
- components.Componentized.__init__(self)
- @implementer(IMeta)
- class ComponentAdder(components.Adapter):
- """
- Adder for componentized adapter tests.
- """
- def __init__(self, original):
- components.Adapter.__init__(self, original)
- self.num = self.original.num
- def add(self, num):
- self.num += num
- return self.num
- class IAttrX(Interface):
- """
- Base interface for test of adapter with C{__cmp__}.
- """
- def x():
- """
- Return a value.
- """
- class IAttrXX(Interface):
- """
- Adapted interface for test of adapter with C{__cmp__}.
- """
- def xx():
- """
- Return a tuple of values.
- """
- @implementer(IAttrX)
- class Xcellent(object):
- """
- L{IAttrX} implementation for test of adapter with C{__cmp__}.
- """
- def x(self):
- """
- Return a value.
- @return: a value
- """
- return 'x!'
- @comparable
- class DoubleXAdapter(object):
- """
- Adapter with __cmp__.
- """
- num = 42
- def __init__(self, original):
- self.original = original
- def xx(self):
- return (self.original.x(), self.original.x())
- def __cmp__(self, other):
- return cmp(self.num, other.num)
- class MetaInterfaceTests(RegistryUsingMixin, unittest.SynchronousTestCase):
- def test_basic(self):
- """
- Registered adapters can be used to adapt classes to an interface.
- """
- components.registerAdapter(MetaAdder, MetaNumber, IMeta)
- n = MetaNumber(1)
- self.assertEqual(IMeta(n).add(1), 2)
- def testComponentizedInteraction(self):
- components.registerAdapter(ComponentAdder, ComponentNumber, IMeta)
- c = ComponentNumber()
- IMeta(c).add(1)
- IMeta(c).add(1)
- self.assertEqual(IMeta(c).add(1), 3)
- def testAdapterWithCmp(self):
- # Make sure that a __cmp__ on an adapter doesn't break anything
- components.registerAdapter(DoubleXAdapter, IAttrX, IAttrXX)
- xx = IAttrXX(Xcellent())
- self.assertEqual(('x!', 'x!'), xx.xx())
- class RegistrationTests(RegistryUsingMixin, unittest.SynchronousTestCase):
- """
- Tests for adapter registration.
- """
- def _registerAdapterForClassOrInterface(self, original):
- """
- Register an adapter with L{components.registerAdapter} for the given
- class or interface and verify that the adapter can be looked up with
- L{components.getAdapterFactory}.
- """
- adapter = lambda o: None
- components.registerAdapter(adapter, original, ITest)
- self.assertIs(
- components.getAdapterFactory(original, ITest, None),
- adapter)
- def test_registerAdapterForClass(self):
- """
- Test that an adapter from a class can be registered and then looked
- up.
- """
- class TheOriginal(object):
- pass
- return self._registerAdapterForClassOrInterface(TheOriginal)
- def test_registerAdapterForInterface(self):
- """
- Test that an adapter from an interface can be registered and then
- looked up.
- """
- return self._registerAdapterForClassOrInterface(ITest2)
- def _duplicateAdapterForClassOrInterface(self, original):
- """
- Verify that L{components.registerAdapter} raises L{ValueError} if the
- from-type/interface and to-interface pair is not unique.
- """
- firstAdapter = lambda o: False
- secondAdapter = lambda o: True
- components.registerAdapter(firstAdapter, original, ITest)
- self.assertRaises(
- ValueError,
- components.registerAdapter,
- secondAdapter, original, ITest)
- # Make sure that the original adapter is still around as well
- self.assertIs(
- components.getAdapterFactory(original, ITest, None),
- firstAdapter)
- def test_duplicateAdapterForClass(self):
- """
- Test that attempting to register a second adapter from a class
- raises the appropriate exception.
- """
- class TheOriginal(object):
- pass
- return self._duplicateAdapterForClassOrInterface(TheOriginal)
- def test_duplicateAdapterForInterface(self):
- """
- Test that attempting to register a second adapter from an interface
- raises the appropriate exception.
- """
- return self._duplicateAdapterForClassOrInterface(ITest2)
- def _duplicateAdapterForClassOrInterfaceAllowed(self, original):
- """
- Verify that when C{components.ALLOW_DUPLICATES} is set to C{True}, new
- adapter registrations for a particular from-type/interface and
- to-interface pair replace older registrations.
- """
- firstAdapter = lambda o: False
- secondAdapter = lambda o: True
- class TheInterface(Interface):
- pass
- components.registerAdapter(firstAdapter, original, TheInterface)
- components.ALLOW_DUPLICATES = True
- try:
- components.registerAdapter(secondAdapter, original, TheInterface)
- self.assertIs(
- components.getAdapterFactory(original, TheInterface, None),
- secondAdapter)
- finally:
- components.ALLOW_DUPLICATES = False
- # It should be rejected again at this point
- self.assertRaises(
- ValueError,
- components.registerAdapter,
- firstAdapter, original, TheInterface)
- self.assertIs(
- components.getAdapterFactory(original, TheInterface, None),
- secondAdapter)
- def test_duplicateAdapterForClassAllowed(self):
- """
- Test that when L{components.ALLOW_DUPLICATES} is set to a true
- value, duplicate registrations from classes are allowed to override
- the original registration.
- """
- class TheOriginal(object):
- pass
- return self._duplicateAdapterForClassOrInterfaceAllowed(TheOriginal)
- def test_duplicateAdapterForInterfaceAllowed(self):
- """
- Test that when L{components.ALLOW_DUPLICATES} is set to a true
- value, duplicate registrations from interfaces are allowed to
- override the original registration.
- """
- class TheOriginal(Interface):
- pass
- return self._duplicateAdapterForClassOrInterfaceAllowed(TheOriginal)
- def _multipleInterfacesForClassOrInterface(self, original):
- """
- Verify that an adapter can be registered for multiple to-interfaces at a
- time.
- """
- adapter = lambda o: None
- components.registerAdapter(adapter, original, ITest, ITest2)
- self.assertIs(
- components.getAdapterFactory(original, ITest, None), adapter)
- self.assertIs(
- components.getAdapterFactory(original, ITest2, None), adapter)
- def test_multipleInterfacesForClass(self):
- """
- Test the registration of an adapter from a class to several
- interfaces at once.
- """
- class TheOriginal(object):
- pass
- return self._multipleInterfacesForClassOrInterface(TheOriginal)
- def test_multipleInterfacesForInterface(self):
- """
- Test the registration of an adapter from an interface to several
- interfaces at once.
- """
- return self._multipleInterfacesForClassOrInterface(ITest3)
- def _subclassAdapterRegistrationForClassOrInterface(self, original):
- """
- Verify that a new adapter can be registered for a particular
- to-interface from a subclass of a type or interface which already has an
- adapter registered to that interface and that the subclass adapter takes
- precedence over the base class adapter.
- """
- firstAdapter = lambda o: True
- secondAdapter = lambda o: False
- class TheSubclass(original):
- pass
- components.registerAdapter(firstAdapter, original, ITest)
- components.registerAdapter(secondAdapter, TheSubclass, ITest)
- self.assertIs(
- components.getAdapterFactory(original, ITest, None),
- firstAdapter)
- self.assertIs(
- components.getAdapterFactory(TheSubclass, ITest, None),
- secondAdapter)
- def test_subclassAdapterRegistrationForClass(self):
- """
- Test that an adapter to a particular interface can be registered
- from both a class and its subclass.
- """
- class TheOriginal(object):
- pass
- return self._subclassAdapterRegistrationForClassOrInterface(TheOriginal)
- def test_subclassAdapterRegistrationForInterface(self):
- """
- Test that an adapter to a particular interface can be registered
- from both an interface and its subclass.
- """
- return self._subclassAdapterRegistrationForClassOrInterface(ITest2)
- class IProxiedInterface(Interface):
- """
- An interface class for use by L{proxyForInterface}.
- """
- ifaceAttribute = Attribute("""
- An example declared attribute, which should be proxied.""")
- def yay(*a, **kw):
- """
- A sample method which should be proxied.
- """
- class IProxiedSubInterface(IProxiedInterface):
- """
- An interface that derives from another for use with L{proxyForInterface}.
- """
- def boo(self):
- """
- A different sample method which should be proxied.
- """
- @implementer(IProxiedInterface)
- class Yayable(object):
- """
- A provider of L{IProxiedInterface} which increments a counter for
- every call to C{yay}.
- @ivar yays: The number of times C{yay} has been called.
- """
- def __init__(self):
- self.yays = 0
- self.yayArgs = []
- def yay(self, *a, **kw):
- """
- Increment C{self.yays}.
- """
- self.yays += 1
- self.yayArgs.append((a, kw))
- return self.yays
- @implementer(IProxiedSubInterface)
- class Booable(object):
- """
- An implementation of IProxiedSubInterface
- """
- yayed = False
- booed = False
- def yay(self):
- """
- Mark the fact that 'yay' has been called.
- """
- self.yayed = True
- def boo(self):
- """
- Mark the fact that 'boo' has been called.1
- """
- self.booed = True
- class IMultipleMethods(Interface):
- """
- An interface with multiple methods.
- """
- def methodOne():
- """
- The first method. Should return 1.
- """
- def methodTwo():
- """
- The second method. Should return 2.
- """
- class MultipleMethodImplementor(object):
- """
- A precise implementation of L{IMultipleMethods}.
- """
- def methodOne(self):
- """
- @return: 1
- """
- return 1
- def methodTwo(self):
- """
- @return: 2
- """
- return 2
- class ProxyForInterfaceTests(unittest.SynchronousTestCase):
- """
- Tests for L{proxyForInterface}.
- """
- def test_original(self):
- """
- Proxy objects should have an C{original} attribute which refers to the
- original object passed to the constructor.
- """
- original = object()
- proxy = proxyForInterface(IProxiedInterface)(original)
- self.assertIs(proxy.original, original)
- def test_proxyMethod(self):
- """
- The class created from L{proxyForInterface} passes methods on an
- interface to the object which is passed to its constructor.
- """
- klass = proxyForInterface(IProxiedInterface)
- yayable = Yayable()
- proxy = klass(yayable)
- proxy.yay()
- self.assertEqual(proxy.yay(), 2)
- self.assertEqual(yayable.yays, 2)
- def test_decoratedProxyMethod(self):
- """
- Methods of the class created from L{proxyForInterface} can be used with
- the decorator-helper L{functools.wraps}.
- """
- base = proxyForInterface(IProxiedInterface)
- class klass(base):
- @wraps(base.yay)
- def yay(self):
- self.original.yays += 1
- return base.yay(self)
- original = Yayable()
- yayable = klass(original)
- yayable.yay()
- self.assertEqual(2, original.yays)
- def test_proxyAttribute(self):
- """
- Proxy objects should proxy declared attributes, but not other
- attributes.
- """
- yayable = Yayable()
- yayable.ifaceAttribute = object()
- proxy = proxyForInterface(IProxiedInterface)(yayable)
- self.assertIs(proxy.ifaceAttribute, yayable.ifaceAttribute)
- self.assertRaises(AttributeError, lambda: proxy.yays)
- def test_proxySetAttribute(self):
- """
- The attributes that proxy objects proxy should be assignable and affect
- the original object.
- """
- yayable = Yayable()
- proxy = proxyForInterface(IProxiedInterface)(yayable)
- thingy = object()
- proxy.ifaceAttribute = thingy
- self.assertIs(yayable.ifaceAttribute, thingy)
- def test_proxyDeleteAttribute(self):
- """
- The attributes that proxy objects proxy should be deletable and affect
- the original object.
- """
- yayable = Yayable()
- yayable.ifaceAttribute = None
- proxy = proxyForInterface(IProxiedInterface)(yayable)
- del proxy.ifaceAttribute
- self.assertFalse(hasattr(yayable, 'ifaceAttribute'))
- def test_multipleMethods(self):
- """
- [Regression test] The proxy should send its method calls to the correct
- method, not the incorrect one.
- """
- multi = MultipleMethodImplementor()
- proxy = proxyForInterface(IMultipleMethods)(multi)
- self.assertEqual(proxy.methodOne(), 1)
- self.assertEqual(proxy.methodTwo(), 2)
- def test_subclassing(self):
- """
- It is possible to subclass the result of L{proxyForInterface}.
- """
- class SpecializedProxy(proxyForInterface(IProxiedInterface)):
- """
- A specialized proxy which can decrement the number of yays.
- """
- def boo(self):
- """
- Decrement the number of yays.
- """
- self.original.yays -= 1
- yayable = Yayable()
- special = SpecializedProxy(yayable)
- self.assertEqual(yayable.yays, 0)
- special.boo()
- self.assertEqual(yayable.yays, -1)
- def test_proxyName(self):
- """
- The name of a proxy class indicates which interface it proxies.
- """
- proxy = proxyForInterface(IProxiedInterface)
- self.assertEqual(
- proxy.__name__,
- "(Proxy for "
- "twisted.python.test.test_components.IProxiedInterface)")
- def test_implements(self):
- """
- The resulting proxy implements the interface that it proxies.
- """
- proxy = proxyForInterface(IProxiedInterface)
- self.assertTrue(IProxiedInterface.implementedBy(proxy))
- def test_proxyDescriptorGet(self):
- """
- _ProxyDescriptor's __get__ method should return the appropriate
- attribute of its argument's 'original' attribute if it is invoked with
- an object. If it is invoked with None, it should return a false
- class-method emulator instead.
- For some reason, Python's documentation recommends to define
- descriptors' __get__ methods with the 'type' parameter as optional,
- despite the fact that Python itself never actually calls the descriptor
- that way. This is probably do to support 'foo.__get__(bar)' as an
- idiom. Let's make sure that the behavior is correct. Since we don't
- actually use the 'type' argument at all, this test calls it the
- idiomatic way to ensure that signature works; test_proxyInheritance
- verifies the how-Python-actually-calls-it signature.
- """
- class Sample(object):
- called = False
- def hello(self):
- self.called = True
- fakeProxy = Sample()
- testObject = Sample()
- fakeProxy.original = testObject
- pd = components._ProxyDescriptor("hello", "original")
- self.assertEqual(pd.__get__(fakeProxy), testObject.hello)
- fakeClassMethod = pd.__get__(None)
- fakeClassMethod(fakeProxy)
- self.assertTrue(testObject.called)
- def test_proxyInheritance(self):
- """
- Subclasses of the class returned from L{proxyForInterface} should be
- able to upcall methods by reference to their superclass, as any normal
- Python class can.
- """
- class YayableWrapper(proxyForInterface(IProxiedInterface)):
- """
- This class does not override any functionality.
- """
- class EnhancedWrapper(YayableWrapper):
- """
- This class overrides the 'yay' method.
- """
- wrappedYays = 1
- def yay(self, *a, **k):
- self.wrappedYays += 1
- return YayableWrapper.yay(self, *a, **k) + 7
- yayable = Yayable()
- wrapper = EnhancedWrapper(yayable)
- self.assertEqual(wrapper.yay(3, 4, x=5, y=6), 8)
- self.assertEqual(yayable.yayArgs,
- [((3, 4), dict(x=5, y=6))])
- def test_interfaceInheritance(self):
- """
- Proxies of subinterfaces generated with proxyForInterface should allow
- access to attributes of both the child and the base interfaces.
- """
- proxyClass = proxyForInterface(IProxiedSubInterface)
- booable = Booable()
- proxy = proxyClass(booable)
- proxy.yay()
- proxy.boo()
- self.assertTrue(booable.yayed)
- self.assertTrue(booable.booed)
- def test_attributeCustomization(self):
- """
- The original attribute name can be customized via the
- C{originalAttribute} argument of L{proxyForInterface}: the attribute
- should change, but the methods of the original object should still be
- callable, and the attributes still accessible.
- """
- yayable = Yayable()
- yayable.ifaceAttribute = object()
- proxy = proxyForInterface(
- IProxiedInterface, originalAttribute='foo')(yayable)
- self.assertIs(proxy.foo, yayable)
- # Check the behavior
- self.assertEqual(proxy.yay(), 1)
- self.assertIs(proxy.ifaceAttribute, yayable.ifaceAttribute)
- thingy = object()
- proxy.ifaceAttribute = thingy
- self.assertIs(yayable.ifaceAttribute, thingy)
- del proxy.ifaceAttribute
- self.assertFalse(hasattr(yayable, 'ifaceAttribute'))
|