1
##############################################################################
3
# Copyright (c) 2003 Zope Corporation and Contributors.
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
13
##############################################################################
14
"""Test base proxy class.
16
$Id: test_proxy.py 87706 2008-06-24 12:23:50Z srichter $
22
from zope.testing.doctestunit import DocTestSuite
23
from zope.proxy import ProxyBase
27
"""This class is expected to be a classic class."""
29
class Comparable(object):
30
def __init__(self, value):
33
def __eq__(self, other):
34
if hasattr(other, "value"):
36
return self.value == other
38
def __ne__(self, other):
39
return not self.__eq__(other)
41
def __lt__(self, other):
42
if hasattr(other, "value"):
44
return self.value < other
46
def __ge__(self, other):
47
return not self.__lt__(other)
49
def __le__(self, other):
50
if hasattr(other, "value"):
52
return self.value <= other
54
def __gt__(self, other):
55
return not self.__le__(other)
58
return "<Comparable: %r>" % self.value
61
class ProxyTestCase(unittest.TestCase):
63
proxy_class = ProxyBase
67
self.p = self.new_proxy(self.x)
69
def new_proxy(self, o):
70
return self.proxy_class(o)
72
def test_constructor(self):
74
self.assertRaises(TypeError, self.proxy_class, o, o)
75
self.assertRaises(TypeError, self.proxy_class, o, key='value')
76
self.assertRaises(TypeError, self.proxy_class, key='value')
78
def test_subclass_constructor(self):
79
class MyProxy(self.proxy_class):
80
def __new__(cls, *args, **kwds):
81
return super(MyProxy, cls).__new__(cls, *args, **kwds)
82
def __init__(self, *args, **kwds):
83
super(MyProxy, self).__init__(*args, **kwds)
88
self.assertEquals(o1, o[0])
89
self.assertEquals(o2, o[1])
91
self.assertRaises(TypeError, MyProxy, o1, o2)
92
self.assertRaises(TypeError, MyProxy, o1, key='value')
93
self.assertRaises(TypeError, MyProxy, key='value')
95
# Check that are passed to __init__() overrides what's passed
97
class MyProxy2(self.proxy_class):
98
def __new__(cls, *args, **kwds):
99
return super(MyProxy2, cls).__new__(cls, 'value')
101
p = MyProxy2('splat!')
102
self.assertEquals(list(p), list('splat!'))
104
class MyProxy3(MyProxy2):
105
def __init__(self, arg):
106
if list(self) != list('value'):
107
raise AssertionError("list(self) != list('value')")
108
super(MyProxy3, self).__init__('another')
110
p = MyProxy3('notused')
111
self.assertEquals(list(p), list('another'))
113
def test_proxy_attributes(self):
116
w = self.new_proxy(o)
117
self.assert_(w.foo == 1)
119
def test___class__(self):
121
w = self.new_proxy(o)
122
self.assert_(w.__class__ is o.__class__)
124
def test_pickle_prevention(self):
125
w = self.new_proxy(Thing())
126
self.assertRaises(pickle.PicklingError,
129
def test_proxy_equality(self):
130
w = self.new_proxy('foo')
131
self.assertEquals(w, 'foo')
135
o3 = Comparable("splat!")
137
w1 = self.new_proxy(o1)
138
w2 = self.new_proxy(o2)
139
w3 = self.new_proxy(o3)
141
self.assertEquals(o1, w1)
142
self.assertEquals(o1, w2)
143
self.assertEquals(o2, w1)
144
self.assertEquals(w1, o2)
145
self.assertEquals(w2, o1)
147
self.assertNotEquals(o3, w1)
148
self.assertNotEquals(w1, o3)
149
self.assertNotEquals(w3, o1)
150
self.assertNotEquals(o1, w3)
152
def test_proxy_ordering_lt(self):
156
w1 = self.new_proxy(o1)
157
w2 = self.new_proxy(o2)
159
self.assert_(w1 < w2)
160
self.assert_(w1 <= w2)
161
self.assert_(o1 < w2)
162
self.assert_(o1 <= w2)
163
self.assert_(w1 < o2)
164
self.assert_(w2 <= o2)
166
def test_proxy_callable(self):
167
w = self.new_proxy({}.get)
168
self.assert_(callable(w))
170
def test_proxy_item_protocol(self):
171
w = self.new_proxy({})
172
self.assertRaises(KeyError, lambda: w[1])
174
self.assertEquals(w[1], 'a')
176
self.assertRaises(KeyError, lambda: w[1])
179
self.assertRaises(KeyError, del_w_1)
181
def test_wrapped_iterable(self):
184
for x in self.new_proxy(a):
186
self.assertEquals(a, b)
188
def test_iteration_over_proxy(self):
189
# Wrap an iterator before starting iteration.
190
# PyObject_GetIter() will still be called on the proxy.
193
for x in self.new_proxy(iter(a)):
195
self.assertEquals(a, b)
196
t = tuple(self.new_proxy(iter(a)))
197
self.assertEquals(t, (1, 2, 3))
199
def test_iteration_using_proxy(self):
200
# Wrap an iterator within the iteration protocol, expecting it
201
# still to work. PyObject_GetIter() will not be called on the
202
# proxy, so the tp_iter slot won't unwrap it.
204
class Iterable(object):
205
def __init__(self, test, data):
209
return self.test.new_proxy(iter(self.data))
213
for x in Iterable(self, a):
215
self.assertEquals(a, b)
217
def test_bool_wrapped_None(self):
218
w = self.new_proxy(None)
219
self.assertEquals(not w, 1)
224
"-x", "+x", "abs(x)", "~x",
225
"int(x)", "long(x)", "float(x)",
228
def test_unops(self):
230
for expr in self.unops:
235
self.assertEqual(z, y,
236
"x=%r; expr=%r" % (x, expr))
238
def test_odd_unops(self):
239
# unops that don't return a proxy
241
for func in hex, oct, lambda x: not x:
242
self.assertEqual(func(P(100)), func(100))
245
"x+y", "x-y", "x*y", "x/y", "divmod(x, y)", "x**y", "x//y",
246
"x<<y", "x>>y", "x&y", "x|y", "x^y",
249
def test_binops(self):
251
for expr in self.binops:
259
self.assertEqual(eval(expr), z,
260
"x=%r; y=%r; expr=%r" % (x, y, expr))
262
def test_inplace(self):
263
# TODO: should test all inplace operators...
268
self.assertEqual(pa, 3)
273
self.failUnless(pa is qa)
274
self.assertEqual(a, [1, 2, 3, 4, 5, 6])
278
self.assertEqual(pa, 4)
280
def test_coerce(self):
283
# Before 2.3, coerce() of two proxies returns them unchanged
284
fixed_coerce = sys.version_info >= (2, 3, 0)
289
self.failUnless(a is x and b is y)
294
self.failUnless(a == 1.0)
295
self.failUnless(b is y)
297
self.failUnless(a.__class__ is float, a.__class__)
302
self.failUnless(a is x)
303
self.failUnless(b == 2.0)
305
self.failUnless(b.__class__ is float, b.__class__)
310
self.failUnless(a is x)
311
self.failUnless(b is y)
316
self.failUnless(a.__class__ is float, a.__class__)
317
self.failUnless(b is y)
322
self.failUnless(a is x)
323
self.failUnless(b.__class__ is float, b.__class__)
328
self.failUnless(a is x)
329
self.failUnless(b is y)
334
self.failUnless(a is x)
335
self.failUnless(b.__class__ is float, b.__class__)
340
self.failUnless(a.__class__ is float, a.__class__)
341
self.failUnless(b is y)
343
def test_getslice(self):
344
# Lists have special slicing bahvior.
345
pList = self.new_proxy([1, 2])
346
self.assertEqual(pList[-1:], [2])
347
self.assertEqual(pList[-2:], [1, 2])
348
self.assertEqual(pList[-3:], [1, 2])
350
# Tuples also have special slicing behavior.
351
pTuple = self.new_proxy((1, 2))
352
self.assertEqual(pTuple[-1:], (2,))
353
self.assertEqual(pTuple[-2:], (1, 2))
354
self.assertEqual(pTuple[-3:], (1, 2))
356
# This behavior should be true for all list- and tuple-derived classes.
357
class DerivedList(list):
359
def __getslice__(self, start, end, step=None):
360
return (start, end, step)
362
pList = self.new_proxy(DerivedList([1, 2]))
363
self.assertEqual(pList[-1:], [2])
364
self.assertEqual(pList[-2:], [1, 2])
365
self.assertEqual(pList[-3:], [1, 2])
367
# Another sort of sequence has a different slicing interpretation.
368
class Slicer(object):
373
def __getslice__(self, start, end, step=None):
374
return (start, end, step)
376
pSlicer = self.new_proxy(Slicer())
377
self.assertEqual(pSlicer[-1:][0], 1)
378
self.assertEqual(pSlicer[-2:][0], 0)
379
# Note that for non-lists and non-tuples the slice is computed
381
self.assertEqual(pSlicer[-3:][0], 1)
383
def test_setslice(self):
384
# Lists have special slicing bahvior for assignment as well.
385
pList = self.new_proxy([1, 2])
387
self.assertEqual(pList, [1, 3, 4])
388
pList = self.new_proxy([1, 2])
390
self.assertEqual(pList, [3, 4])
391
pList = self.new_proxy([1, 2])
393
self.assertEqual(pList, [3, 4])
395
# This behavior should be true for all list-derived classes.
396
class DerivedList(list):
399
pList = self.new_proxy(DerivedList([1, 2]))
401
self.assertEqual(pList, [1, 3, 4])
402
pList = self.new_proxy(DerivedList([1, 2]))
404
self.assertEqual(pList, [3, 4])
405
pList = self.new_proxy(DerivedList([1, 2]))
407
self.assertEqual(pList, [3, 4])
412
>>> from zope.proxy import ProxyBase, isProxy
413
>>> class P1(ProxyBase):
415
>>> class P2(ProxyBase):
425
>>> int(isProxy(p, P1))
427
>>> int(isProxy(p, P2))
430
>>> int(isProxy(p, P1))
432
>>> int(isProxy(p, P2))
437
def test_getProxiedObject():
439
>>> from zope.proxy import ProxyBase, getProxiedObject
443
>>> int(getProxiedObject(c) is c)
446
>>> int(getProxiedObject(p) is c)
448
>>> p2 = ProxyBase(p)
449
>>> int(getProxiedObject(p2) is p)
454
def test_ProxyIterator():
456
>>> from zope.proxy import ProxyBase, ProxyIterator
460
>>> p1 = ProxyBase(c)
461
>>> class P(ProxyBase):
464
>>> p3 = ProxyBase(p2)
465
>>> list(ProxyIterator(p3)) == [p3, p2, p1, c]
469
def test_removeAllProxies():
471
>>> from zope.proxy import ProxyBase, removeAllProxies
475
>>> int(removeAllProxies(c) is c)
478
>>> int(removeAllProxies(p) is c)
480
>>> p2 = ProxyBase(p)
481
>>> int(removeAllProxies(p2) is c)
486
def test_queryProxy():
488
>>> from zope.proxy import ProxyBase, queryProxy
489
>>> class P1(ProxyBase):
491
>>> class P2(ProxyBase):
496
>>> queryProxy(c, P1)
497
>>> queryProxy(c, P1, 42)
500
>>> int(queryProxy(p1, P1) is p1)
502
>>> queryProxy(c, P2)
503
>>> queryProxy(c, P2, 42)
506
>>> int(queryProxy(p2, P1) is p1)
508
>>> int(queryProxy(p2, P2) is p2)
510
>>> int(queryProxy(p2, ProxyBase) is p2)
515
def test_queryInnerProxy():
517
>>> from zope.proxy import ProxyBase, queryProxy, queryInnerProxy
518
>>> class P1(ProxyBase):
520
>>> class P2(ProxyBase):
525
>>> queryInnerProxy(c, P1)
526
>>> queryInnerProxy(c, P1, 42)
529
>>> int(queryProxy(p1, P1) is p1)
531
>>> queryInnerProxy(c, P2)
532
>>> queryInnerProxy(c, P2, 42)
535
>>> int(queryInnerProxy(p2, P1) is p1)
537
>>> int(queryInnerProxy(p2, P2) is p2)
539
>>> int(queryInnerProxy(p2, ProxyBase) is p1)
543
>>> int(queryProxy(p3, P1) is p3)
545
>>> int(queryInnerProxy(p3, P1) is p1)
547
>>> int(queryInnerProxy(p3, P2) is p2)
552
def test_sameProxiedObjects():
554
>>> from zope.proxy import ProxyBase, sameProxiedObjects
559
>>> int(sameProxiedObjects(c1, c1))
561
>>> int(sameProxiedObjects(ProxyBase(c1), c1))
563
>>> int(sameProxiedObjects(ProxyBase(c1), ProxyBase(c1)))
565
>>> int(sameProxiedObjects(ProxyBase(ProxyBase(c1)), c1))
567
>>> int(sameProxiedObjects(c1, ProxyBase(c1)))
569
>>> int(sameProxiedObjects(c1, ProxyBase(ProxyBase(c1))))
571
>>> int(sameProxiedObjects(c1, c2))
573
>>> int(sameProxiedObjects(ProxyBase(c1), c2))
575
>>> int(sameProxiedObjects(ProxyBase(c1), ProxyBase(c2)))
577
>>> int(sameProxiedObjects(ProxyBase(ProxyBase(c1)), c2))
579
>>> int(sameProxiedObjects(c1, ProxyBase(c2)))
581
>>> int(sameProxiedObjects(c1, ProxyBase(ProxyBase(c2))))
585
def test_subclassing_proxies():
586
"""You can subclass ProxyBase
588
If you subclass a proxy, instances of the subclass have access to
589
data defined in the class, including descriptors.
591
Your subclass instances don't get instance dictionaries, but they
594
>>> class MyProxy(ProxyBase):
595
... __slots__ = 'x', 'y'
603
I can use attributes defined by the class, including slots:
611
I can also use attributes of the proxied object:
622
def test_get_descriptors_in_proxy_class():
624
A non-data descriptor in a proxy class doesn't hide an attribute on
625
a proxied object or prevent writing the attribute.
627
>>> class ReadDescr(object):
628
... def __get__(self, i, c):
631
>>> class MyProxy(ProxyBase):
654
def test_non_overridable():
656
Normally, methods defined in proxies are overridden by
657
methods of proxied objects. This applies to all non-data
658
descriptors. The non_overridable function can be used to
659
convert a non-data descriptor to a data descriptor that disallows
660
writes. This function can be used as a decorator to make functions
661
defined in proxy classes take precedence over functions defined
665
>>> class MyProxy(ProxyBase):
668
... @zope.proxy.non_overridable
670
... return 'MyProxy foo'
674
... return 'MyOb foo'
683
def test_setProxiedObject():
685
>>> from zope.proxy import ProxyBase
686
>>> from zope.proxy import setProxiedObject, getProxiedObject
694
>>> p = ProxyBase(c1)
696
`setProxiedObject()` allows us to change the object a proxy refers to,
697
returning the previous referent:
699
>>> old = setProxiedObject(p, c2)
703
>>> getProxiedObject(p) is c2
706
The first argument to `setProxiedObject()` must be a proxy; other objects
707
cause it to raise an exception:
709
>>> setProxiedObject(c1, None)
710
Traceback (most recent call last):
711
TypeError: setProxiedObject() argument 1 must be zope.proxy.ProxyBase, not C
716
suite = unittest.makeSuite(ProxyTestCase)
717
suite.addTest(DocTestSuite())
720
if __name__ == "__main__":
721
runner = unittest.TextTestRunner(sys.stdout)
722
result = runner.run(test_suite())
723
newerrs = len(result.errors) + len(result.failures)
724
sys.exit(newerrs and 1 or 0)