[Zope3-checkins] CVS: Zope3/src/zope/proxy/context/tests - test_decorator.py:1.1

Steve Alexander steve@cat-box.net
Thu, 8 May 2003 06:57:01 -0400


Update of /cvs-repository/Zope3/src/zope/proxy/context/tests
In directory cvs.zope.org:/tmp/cvs-serv18548/src/zope/proxy/context/tests

Added Files:
	test_decorator.py 
Log Message:
Add new decorator type, with tests.
Add this to the build directives in setup.py.



=== Added File Zope3/src/zope/proxy/context/tests/test_decorator.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: test_decorator.py,v 1.1 2003/05/08 10:56:59 stevea Exp $
"""
import unittest

from zope.proxy.context import wrapper, decorator
from zope.proxy.context.tests.test_wrapper import WrapperTestCase

class DecoratorTestCase(WrapperTestCase):

    proxy_class = decorator.Decorator

    def new_proxy(self, o, c=None, mixinfactory=None, names=(), provides=None):
        return self.proxy_class(o, c, mixinfactory, names, provides)

    def test_subclass_constructor(self):
        class MyWrapper(self.proxy_class):
            def __init__(self, *args, **kwds):
                super(MyWrapper, self).__init__('foo', **kwds)

        w = MyWrapper(1, 2, key='value')
        self.assertEquals(wrapper.getobject(w), 'foo')
        self.assertEquals(wrapper.getdict(w), {'key': 'value'})

        # __new__ catches too many positional args:
        self.assertRaises(TypeError, MyWrapper, 1, 2, 3, 4, 5, 6)

    def test_decorator_basics(self):
        # check that default arguments are set correctly as per the interface
        obj = object()
        w = self.proxy_class(obj)
        self.assert_(wrapper.getcontext(w) is None)
        self.assert_(decorator.getmixin(w) is None)
        self.assertEquals(decorator.getnames(w), ())
        self.assert_(decorator.getprovides(w) is None)
        self.assert_(decorator.getmixinfactory(w) is None)

        # check that non-default arguments are set correctly
        class SomeObject(object):
            def bar(self):
                pass
        obj = SomeObject()

        class MixinFactory(object):
            def foo(self):
                pass
            def bar(self):
                pass

        c = object()
        f = MixinFactory
        n = ('foo',)
        p = object()
        w = self.proxy_class(obj, c, f, n, p)

        # getnamesdict is not in the official decorator interface, but it
        # is provided so that the caching dict can be unit-tested from Python.
        self.assertEquals(decorator.getnamesdict(w).keys(), ['foo',])

        self.assert_(wrapper.getcontext(w) is c)
        self.assert_(decorator.getmixin(w) is None)
        self.assertEquals(decorator.getnames(w), n)
        self.assert_(decorator.getprovides(w) is p)
        self.assert_(decorator.getmixinfactory(w) is f)

        # Check that accessing a non-name does not create the mixin.
        w.bar()
        self.assert_(decorator.getmixin(w) is None)
        # Check that accessing a name creates the mixin.
        w.foo()
        self.assert_(type(decorator.getmixin(w)) is MixinFactory)

        # check set and get provides
        decorator.setprovides(w, None)
        self.assert_(decorator.getprovides(w) is None)
        decorator.setprovides(w, p)
        self.assert_(decorator.getprovides(w) is p)

        # check that getmixincreate works
        w = self.proxy_class(obj, c, f, n, p)
        self.assert_(decorator.getmixin(w) is None)
        mixin = decorator.getmixincreate(w)
        self.assert_(type(mixin) is MixinFactory)
        self.assert_(decorator.getmixin(w) is mixin)

    def test_mixin_created_once_only(self):
        class SomeObject(object):
            def bar(self):
                pass
        obj = SomeObject()

        class MixinFactory(object):
            def foo(self):
                pass
            def bar(self):
                pass

        c = object()
        f = MixinFactory
        n = ('foo',)
        p = object()
        w = self.proxy_class(obj, c, f, n, p)

        self.assert_(decorator.getmixin(w) is None)
        self.assert_(decorator.getmixinfactory(w) is f)

        w.foo()
        mixin = decorator.getmixin(w)
        self.assert_(type(mixin) is MixinFactory)
        w.foo()
        mixin2 = decorator.getmixin(w)
        self.assert_(mixin is mixin2)

    def test_typeerror_if_no_factory(self):
        w = self.proxy_class(object(), None, None, ('foo',))
        self.assertRaises(TypeError, getattr, w, 'foo')
        self.assertRaises(TypeError, decorator.getmixincreate)

    def test_decorator_setattr(self):
        obj = object()

        class MixinFactory(object):
            def setFoo(self, value):
                self.fooval = value
            def getFoo(self):
                return self.fooval
            def delFoo(self):
                del self.fooval
            foo = property(getFoo, setFoo, delFoo)

        w = self.proxy_class(obj, None, MixinFactory, ('foo',))
        mixin = decorator.getmixincreate(w)
        self.failIf(hasattr(mixin, 'fooval'))
        self.assertRaises(AttributeError, getattr, w, 'foo')
        self.assertRaises(AttributeError, delattr, w, 'foo')
        w.foo = 'skidoo'
        self.assertEquals(mixin.fooval, 'skidoo')
        del w.foo
        self.failIf(hasattr(mixin, 'fooval'))

    def test_decorated_slots(self):
        obj = object()

        names = ('__len__', '__getitem__', '__setitem__', '__str__',
                 '__contains__', '__call__', '__nonzero__', '__iter__',
                 'next')

        dummy_iter = iter(range(5))

        class MixinFactory(object):
            count = 0
            def __len__(self):
                self.called = 'len'
                return 5
            def __nonzero__(self):
                self.called = 'nonzero'
                return False
            def __getitem__(self, key):
                self.called = 'getitem'
                return 5
            def __setitem__(self, key, value):
                self.called = 'setitem'
            def __str__(self):
                self.called = 'str'
                return '5'
            def __contains__(self, key):
                self.called = 'contains'
                return True
            def __call__(self):
                self.called = 'call'
                return 'skidoo'
            def __iter__(self):
                self.called = 'iter'
                return self
            def next(self):
                self.called = 'next'
                self.count += 1
                if self.count == 5:
                    self.count = 0
                    raise StopIteration
                return self.count

        w = self.proxy_class(obj, None, MixinFactory, names)

        mixin = decorator.getmixincreate(w)
        self.assertRaises(AttributeError, getattr, mixin, 'called')

        self.assertEquals(len(w), 5)
        self.assertEquals(mixin.called, 'len')
        del mixin.called

        self.assertEquals(w[3], 5)
        self.assertEquals(mixin.called, 'getitem')
        del mixin.called

        w[3] = 5
        self.assertEquals(mixin.called, 'setitem')
        del mixin.called

        self.assertEquals(str(w), '5')
        self.assertEquals(mixin.called, 'str')
        del mixin.called

        self.assert_(5 in w)
        self.assertEquals(mixin.called, 'contains')
        del mixin.called

        self.assertEquals(w(), 'skidoo')
        self.assertEquals(mixin.called, 'call')
        del mixin.called

        self.assertEquals(bool(w), False)
        self.assertEquals(mixin.called, 'nonzero')
        del mixin.called

        # Test case where mixin doesn't provide a __nonzero__ but does
        # provide a __len__.
        del MixinFactory.__nonzero__
        self.assertEquals(bool(w), True)
        self.assertEquals(mixin.called, 'len')
        del mixin.called

        self.assertEquals(iter(w), mixin)
        self.assertEquals(mixin.called, 'iter')
        del mixin.called

        self.assertEquals(w.next(), 1)
        self.assertEquals(mixin.called, 'next')
        self.assertEquals([i for i in iter(w)], [2, 3, 4])
        del mixin.called

    def test_decorated_iterable(self):
        obj = object()
        a = [1, 2, 3]
        b = []
        factory = lambda: a
        names = ('__iter__',)
        for x in self.proxy_class(obj, None, factory, names):
            b.append(x)
        self.assertEquals(a, b)

    def test_iteration_over_decorator(self):
        # Wrap an iterator before starting iteration.
        # PyObject_GetIter() will still be called on the proxy.
        obj = object()
        a = [1, 2, 3]
        b = []
        factory = lambda: iter(a)
        names = ('__iter__',)
        for x in self.proxy_class(obj, None, factory, names):
            b.append(x)
        self.assertEquals(a, b)
        t = tuple(self.proxy_class(obj, None, factory, names))
        self.assertEquals(t, (1, 2, 3))

    def XXXtest_iteration_using_decorator(self):
        # XXX This test is taken from test_iteration_using_proxy in
        # test_proxy.py. It doesn't work when adapted for decoration.
        # This needs looking at, but it is something of an edge case, and
        # so isn't a priority.

        # Wrap an iterator within the iteration protocol, expecting it
        # still to work.  PyObject_GetIter() will not be called on the
        # proxy, so the tp_iter slot won't unwrap it.

        class Iterable:
            def __init__(self, test, data):
                self.test = test
                self.data = data
            def __iter__(self):
                obj = object()
                names = ('__iter__', 'next')
                factory = lambda: iter(self.data)
                return self.test.proxy_class(obj, None, factory, names)

        a = [1, 2, 3]
        b = []
        for x in Iterable(self, a):
            b.append(x)
        self.assertEquals(a, b)

def test_suite():
    return unittest.makeSuite(DecoratorTestCase)


if __name__ == "__main__":
    import sys
    runner = unittest.TextTestRunner(sys.stdout)
    result = runner.run(test_suite())
    newerrs = len(result.errors) + len(result.failures)
    sys.exit(newerrs and 1 or 0)