[Zope3-checkins] CVS: Zope3/src/zope/context/tests - test_wrapper.py:1.16
Steve Alexander
steve@cat-box.net
Thu, 29 May 2003 05:07:06 -0400
Update of /cvs-repository/Zope3/src/zope/context/tests
In directory cvs.zope.org:/tmp/cvs-serv30464/src/zope/context/tests
Modified Files:
test_wrapper.py
Log Message:
Wrappers now allow subclasses to supply new methods and other descriptors,
and to override existing slots and methods.
=== Zope3/src/zope/context/tests/test_wrapper.py 1.15 => 1.16 ===
--- Zope3/src/zope/context/tests/test_wrapper.py:1.15 Wed May 28 13:19:23 2003
+++ Zope3/src/zope/context/tests/test_wrapper.py Thu May 29 05:06:35 2003
@@ -18,12 +18,46 @@
import unittest
from zope.proxy import getProxiedObject
-from zope.context import wrapper, getcontext, ContextWrapper
+from zope.context import ContextWrapper, wrapper
from zope.context import ContextMethod, ContextProperty, ContextAware
from zope.proxy.tests.test_proxy import Thing, ProxyTestCase
_marker = object()
+class WrapperProperty(object):
+ def __init__(self, name, dictname, default=_marker):
+ self.name = name
+ self.dictname = dictname
+ self.default = default
+
+ def __get__(self, obj, tp=None):
+ if obj is None:
+ return self
+ d = wrapper.getdict(obj)
+ if d:
+ try:
+ return d[self.dictname]
+ except KeyError:
+ pass
+ if self.default is not _marker:
+ return self.default
+ raise AttributeError, self.name
+
+ def __set__(self, obj, value):
+ self.default = _marker
+ wrapper.getdictcreate(obj)[self.dictname] = value
+
+ def __delete__(self, obj):
+ self.default = _marker
+ d = wrapper.getdict(obj)
+ if d:
+ try:
+ del d[self.dictname]
+ return
+ except KeyError:
+ pass
+ raise AttributeError, self.name
+
class WrapperTestCase(ProxyTestCase):
proxy_class = wrapper.Wrapper
@@ -92,7 +126,7 @@
context = object()
def doit(self, *args):
- self.retval = getcontext(self), args
+ self.retval = wrapper.getcontext(self), args
if fixed_retval is _marker:
return self.retval
else:
@@ -484,10 +518,203 @@
w = self.new_proxy(Thing())
self.assertRaises(pickle.PicklingError,
pickle.dumps, w)
+ try:
+ pickle.dumps(w)
+ except pickle.PicklingError, err:
+ # We need to check that the error is the one raised by the
+ # Wrapper's __reduce__ method, and not one caused by the pickling
+ # machinery getting confused.
+ self.assertEquals(err[0], "Wrapper instances cannot be pickled.")
+
+ def test_reduce_in_subclass(self):
+ class CustomPicklingError(pickle.PicklingError):
+ pass
+ class WrapperWithReduce(self.proxy_class):
+ def __reduce__(self):
+ raise CustomPicklingError
+ w = WrapperWithReduce(Thing())
+ self.assertRaises(CustomPicklingError, pickle.dumps, w)
+
+ def test_simple_subclass(self):
+
+ class Foo(self.proxy_class):
+ def bar(self, *args):
+ inner = getProxiedObject(self)
+ inner += args
+ return len(args)
+
+ baz = 23
+ spoo = WrapperProperty('spoo', 'spooprop', default=23)
+
+ self.assert_(self.proxy_class in Foo.__mro__)
+
+ l = []
+ w = Foo(l)
+ self.assert_(w.__class__ is l.__class__)
+ self.assert_(type(w) is Foo)
+
+ self.assertEquals(w.bar(1, 2, 3), 3)
+ self.assertEquals(l, [1, 2, 3])
+ w.append('x')
+ self.assertEquals(l, [1, 2, 3, 'x'])
+
+ self.assertEquals(w.spoo, 23)
+ w.spoo = 24
+ self.assertEquals(w.spoo, 24)
+
+ self.assertEquals(w.baz, 23)
+ self.assertRaises(TypeError, setattr, w, 'baz', 24)
+
+ def test_subclass_with_slots(self):
+ obj = object()
+
+ names = ('__len__', '__getitem__', '__setitem__', '__str__',
+ '__contains__', '__call__', '__nonzero__', '__iter__',
+ 'next')
+
+ dummy_iter = iter(range(5))
+ proxy_class = self.proxy_class
+ class WrapperWithSlots(proxy_class):
+ def __init__(self, *args, **kw):
+ super(WrapperWithSlots, self).__init__(*args, **kw)
+ d = wrapper.getdictcreate(self)
+ d['initargs'] = args
+ d['initkw'] = kw
+
+ count = WrapperProperty('count', 'wrapper_count', default=0)
+ called = WrapperProperty('called', 'wrapper_called')
+ 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 = WrapperWithSlots(obj, None)
+
+ self.assertEquals(len(w), 5)
+ self.assertEquals(w.called, 'len')
+ del w.called
+
+ self.assertEquals(w[3], 5)
+ self.assertEquals(w.called, 'getitem')
+ del w.called
+
+ w[3] = 5
+ self.assertEquals(w.called, 'setitem')
+ del w.called
+
+ self.assertEquals(str(w), '5')
+ self.assertEquals(w.called, 'str')
+ del w.called
+
+ self.assert_(5 in w)
+ self.assertEquals(w.called, 'contains')
+ del w.called
+
+ self.assertEquals(w(), 'skidoo')
+ self.assertEquals(w.called, 'call')
+ del w.called
+
+ self.assertEquals(bool(w), False)
+ self.assertEquals(w.called, 'nonzero')
+ del w.called
+
+ # Test case where w doesn't provide a __nonzero__ but does
+ # provide a __len__.
+ del WrapperWithSlots.__nonzero__
+ self.assertEquals(bool(w), True)
+ self.assertEquals(w.called, 'len')
+ del w.called
+
+ self.assertEquals(iter(w), w)
+ self.assertEquals(w.called, 'iter')
+ del w.called
+
+ self.assertEquals(w.next(), 1)
+ self.assertEquals(w.called, 'next')
+ self.assertEquals([i for i in iter(w)], [2, 3, 4])
+ del w.called
+
+ def test_decorated_iterable(self):
+ obj = object()
+ a = [1, 2, 3]
+ b = []
+ class IterableDecorator(self.proxy_class):
+ def __iter__(self):
+ return iter(a)
+ for x in IterableDecorator(obj):
+ b.append(x)
+ self.assertEquals(a, b)
+
+ def test_iteration_using_decorator(self):
+ # 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()
+ it = iter(self.data)
+ class IterableDecorator(self.test.proxy_class):
+ def __iter__(self):
+ return it.__iter__()
+ def next(self):
+ return it.next()
+ return IterableDecorator(obj)
+
+ a = [1, 2, 3]
+ b = []
+ for x in Iterable(self, a):
+ b.append(x)
+ self.assertEquals(a, b)
+
+
+class WrapperSubclass(wrapper.Wrapper):
+
+ def someMethodOrOther(self):
+ pass
+
+class WrapperSubclassTestCase(WrapperTestCase):
+
+ proxy_class = WrapperSubclass
+
+ def new_proxy(self, o, c=None):
+ return self.proxy_class(o, c)
def test_suite():
- return unittest.makeSuite(WrapperTestCase)
+ return unittest.TestSuite((
+ unittest.makeSuite(WrapperTestCase),
+ unittest.makeSuite(WrapperSubclassTestCase),
+ ))
if __name__ == "__main__":