d0df704d8a
added python.path vm arg to startup script fixed infinite loop in unwrap() when displaying sequences of sequences git-svn-id: http://google-refine.googlecode.com/svn/trunk@509 7d457c2a-affb-35e4-300a-418c747d4874
549 lines
15 KiB
Python
549 lines
15 KiB
Python
"""Test descriptors, binary ops, etc.
|
|
|
|
Made for Jython.
|
|
"""
|
|
import types
|
|
import unittest
|
|
from test import test_support
|
|
|
|
class Old:
|
|
pass
|
|
|
|
|
|
class New(object):
|
|
pass
|
|
|
|
|
|
old = Old()
|
|
new = New()
|
|
|
|
|
|
class TestDescrTestCase(unittest.TestCase):
|
|
|
|
def test_class_dict_is_copy(self):
|
|
class FooMeta(type):
|
|
def __new__(meta, name, bases, class_dict):
|
|
cls = type.__new__(meta, name, bases, class_dict)
|
|
self.assert_('foo' not in class_dict)
|
|
cls.foo = 'bar'
|
|
self.assert_('foo' not in class_dict)
|
|
return cls
|
|
|
|
class Foo(object):
|
|
__metaclass__ = FooMeta
|
|
|
|
def test_descr___get__(self):
|
|
class Foo(object):
|
|
__slots__ = 'bar'
|
|
def hello(self):
|
|
pass
|
|
def hi(self):
|
|
pass
|
|
hi = staticmethod(hi)
|
|
foo = Foo()
|
|
foo.bar = 'baz'
|
|
|
|
self.assertEqual(Foo.bar.__get__(foo), 'baz')
|
|
self.assertEqual(Foo.bar.__get__(None, Foo), Foo.bar)
|
|
|
|
bound = Foo.hello.__get__(foo)
|
|
self.assert_(isinstance(bound, types.MethodType))
|
|
self.assert_(bound.im_self is foo)
|
|
self.assertEqual(Foo.hello.__get__(None, Foo), Foo.hello)
|
|
|
|
bound = Foo.hi.__get__(foo)
|
|
self.assert_(isinstance(bound, types.MethodType))
|
|
self.assert_(bound.im_self is foo)
|
|
unbound = Foo.hi.__get__(None, foo)
|
|
self.assert_(isinstance(unbound, types.MethodType))
|
|
self.assert_(unbound.im_self is None)
|
|
|
|
def test_ints(self):
|
|
class C(int):
|
|
pass
|
|
try:
|
|
foo = int(None)
|
|
except TypeError:
|
|
pass
|
|
else:
|
|
self.assert_(False, "should have raised TypeError")
|
|
try:
|
|
foo = C(None)
|
|
except TypeError:
|
|
pass
|
|
else:
|
|
self.assert_(False, "should have raised TypeError")
|
|
|
|
def test_raising_custom_attribute_error(self):
|
|
class RaisesCustomMsg(object):
|
|
def __get__(self, instance, type):
|
|
raise AttributeError("Custom message")
|
|
|
|
|
|
class CustomAttributeError(AttributeError): pass
|
|
|
|
class RaisesCustomErr(object):
|
|
def __get__(self, instance, type):
|
|
raise CustomAttributeError
|
|
|
|
class Foo(object):
|
|
custom_msg = RaisesCustomMsg()
|
|
custom_err = RaisesCustomErr()
|
|
|
|
self.assertRaises(CustomAttributeError, lambda: Foo().custom_err)
|
|
try:
|
|
Foo().custom_msg
|
|
self.assert_(False) # Previous line should raise AttributteError
|
|
except AttributeError, e:
|
|
self.assertEquals("Custom message", str(e))
|
|
|
|
|
|
class SubclassDescrTestCase(unittest.TestCase):
|
|
|
|
def test_subclass_cmp_right_op(self):
|
|
# Case 1: subclass of int
|
|
|
|
class B(int):
|
|
def __ge__(self, other):
|
|
return "B.__ge__"
|
|
def __le__(self, other):
|
|
return "B.__le__"
|
|
|
|
self.assertEqual(B(1) >= 1, "B.__ge__")
|
|
self.assertEqual(1 >= B(1), "B.__le__")
|
|
|
|
# Case 2: subclass of object
|
|
|
|
class C(object):
|
|
def __ge__(self, other):
|
|
return "C.__ge__"
|
|
def __le__(self, other):
|
|
return "C.__le__"
|
|
|
|
self.assertEqual(C() >= 1, "C.__ge__")
|
|
self.assertEqual(1 >= C(), "C.__le__")
|
|
|
|
# Case 3: subclass of new-style class; here it gets interesting
|
|
|
|
class D(C):
|
|
def __ge__(self, other):
|
|
return "D.__ge__"
|
|
def __le__(self, other):
|
|
return "D.__le__"
|
|
|
|
self.assertEqual(D() >= C(), "D.__ge__")
|
|
self.assertEqual(C() >= D(), "D.__le__")
|
|
|
|
# Case 4: comparison is different than other binops
|
|
|
|
class E(C):
|
|
pass
|
|
|
|
self.assertEqual(E.__le__, C.__le__)
|
|
|
|
self.assertEqual(E() >= 1, "C.__ge__")
|
|
self.assertEqual(1 >= E(), "C.__le__")
|
|
self.assertEqual(E() >= C(), "C.__ge__")
|
|
self.assertEqual(C() >= E(), "C.__le__") # different
|
|
|
|
def test_subclass_binop(self):
|
|
def raises(exc, expected, callable, *args):
|
|
try:
|
|
callable(*args)
|
|
except exc, msg:
|
|
if str(msg) != expected:
|
|
self.assert_(False, "Message %r, expected %r" % (str(msg),
|
|
expected))
|
|
else:
|
|
self.assert_(False, "Expected %s" % exc)
|
|
|
|
class B(object):
|
|
pass
|
|
|
|
class C(object):
|
|
def __radd__(self, o):
|
|
return '%r + C()' % (o,)
|
|
|
|
def __rmul__(self, o):
|
|
return '%r * C()' % (o,)
|
|
|
|
# Test strs, unicode, lists and tuples
|
|
mapping = []
|
|
|
|
# + binop
|
|
mapping.append((lambda o: 'foo' + o,
|
|
TypeError, "cannot concatenate 'str' and 'B' objects",
|
|
"'foo' + C()"))
|
|
# XXX: There's probably work to be done here besides just emulating this
|
|
# message
|
|
if test_support.is_jython:
|
|
mapping.append((lambda o: u'foo' + o,
|
|
TypeError, "cannot concatenate 'unicode' and 'B' objects",
|
|
"u'foo' + C()"))
|
|
else:
|
|
mapping.append((lambda o: u'foo' + o,
|
|
TypeError,
|
|
'coercing to Unicode: need string or buffer, B found',
|
|
"u'foo' + C()"))
|
|
mapping.append((lambda o: [1, 2] + o,
|
|
TypeError, 'can only concatenate list (not "B") to list',
|
|
'[1, 2] + C()'))
|
|
mapping.append((lambda o: ('foo', 'bar') + o,
|
|
TypeError, 'can only concatenate tuple (not "B") to tuple',
|
|
"('foo', 'bar') + C()"))
|
|
|
|
# * binop
|
|
mapping.append((lambda o: 'foo' * o,
|
|
TypeError, "can't multiply sequence by non-int of type 'B'",
|
|
"'foo' * C()"))
|
|
mapping.append((lambda o: u'foo' * o,
|
|
TypeError, "can't multiply sequence by non-int of type 'B'",
|
|
"u'foo' * C()"))
|
|
mapping.append((lambda o: [1, 2] * o,
|
|
TypeError, "can't multiply sequence by non-int of type 'B'",
|
|
'[1, 2] * C()'))
|
|
mapping.append((lambda o: ('foo', 'bar') * o,
|
|
TypeError, "can't multiply sequence by non-int of type 'B'",
|
|
"('foo', 'bar') * C()"))
|
|
|
|
for func, bexc, bexc_msg, cresult in mapping:
|
|
raises(bexc, bexc_msg, lambda : func(B()))
|
|
self.assertEqual(func(C()), cresult)
|
|
|
|
def test_overriding_base_binop(self):
|
|
class MulBase(object):
|
|
def __init__(self, value):
|
|
self.value = value
|
|
def __mul__(self, other):
|
|
return self.value * other.value
|
|
def __rmul__(self, other):
|
|
return other.value * self.value
|
|
class DoublerBase(MulBase):
|
|
def __mul__(self, other):
|
|
return 2 * (self.value * other.value)
|
|
class AnotherDoubler(DoublerBase):
|
|
pass
|
|
self.assertEquals(DoublerBase(2) * AnotherDoubler(3), 12)
|
|
|
|
def test_oldstyle_binop_notimplemented(self):
|
|
class Foo:
|
|
pass
|
|
class Bar(object):
|
|
def __radd__(self, other):
|
|
return 3
|
|
self.assertEqual(Foo() + Bar(), 3)
|
|
|
|
def test_int_mul(self):
|
|
# http://bugs.jython.org/issue1332
|
|
class Foo(tuple):
|
|
def __rmul__(self, other):
|
|
return 'foo'
|
|
foo = Foo()
|
|
self.assertEqual(3.0 * foo, 'foo')
|
|
self.assertEqual(4 * foo, 'foo')
|
|
|
|
|
|
class InPlaceTestCase(unittest.TestCase):
|
|
|
|
def test_iadd(self):
|
|
class Foo(object):
|
|
def __add__(self, other):
|
|
return 1
|
|
def __radd__(self, other):
|
|
return 2
|
|
class Bar(object):
|
|
pass
|
|
class Baz(object):
|
|
def __iadd__(self, other):
|
|
return NotImplemented
|
|
foo = Foo()
|
|
foo += Bar()
|
|
self.assertEqual(foo, 1)
|
|
bar = Bar()
|
|
bar += Foo()
|
|
self.assertEqual(bar, 2)
|
|
baz = Baz()
|
|
baz += Foo()
|
|
self.assertEqual(baz, 2)
|
|
|
|
def test_imul(self):
|
|
class FooInplace(list):
|
|
def __imul__(self, other):
|
|
return [1]
|
|
class Bar(FooInplace):
|
|
def __mul__(self, other):
|
|
return [2]
|
|
foo = FooInplace()
|
|
foo *= 3
|
|
self.assertEqual(foo, [1])
|
|
foo = Bar([3])
|
|
foo *= 3
|
|
self.assertEqual(foo, [1])
|
|
|
|
class Baz(FooInplace):
|
|
def __mul__(self, other):
|
|
return [3]
|
|
baz = Baz()
|
|
baz *= 3
|
|
self.assertEqual(baz, [1])
|
|
|
|
def test_list(self):
|
|
class Foo(list):
|
|
def __mul__(self, other):
|
|
return [1]
|
|
foo = Foo([2])
|
|
foo *= 3
|
|
if test_support.is_jython:
|
|
self.assertEqual(foo, [2, 2, 2])
|
|
else:
|
|
# CPython ignores list.__imul__ on a subclass with __mul__
|
|
# (unlike Jython and PyPy)
|
|
self.assertEqual(foo, [1])
|
|
|
|
class Bar(object):
|
|
def __radd__(self, other):
|
|
return 1
|
|
def __rmul__(self, other):
|
|
return 2
|
|
l = []
|
|
l += Bar()
|
|
self.assertEqual(l, 1)
|
|
l = []
|
|
l *= Bar()
|
|
self.assertEqual(l, 2)
|
|
|
|
def test_iand(self):
|
|
# Jython's set __iand__ (as well as isub, ixor, etc) was
|
|
# previously broken
|
|
class Foo(set):
|
|
def __and__(self, other):
|
|
return set([1])
|
|
foo = Foo()
|
|
foo &= 3
|
|
self.assertEqual(foo, set([1]))
|
|
|
|
|
|
class DescrExceptionsTestCase(unittest.TestCase):
|
|
|
|
def test_hex(self):
|
|
self._test(hex)
|
|
|
|
def test_oct(self):
|
|
self._test(oct)
|
|
|
|
def test_other(self):
|
|
for op in '-', '+', '~':
|
|
try:
|
|
eval('%s(old)' % op)
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
self._assert(False, 'Expected an AttributeError, op: %s' % op)
|
|
try:
|
|
eval('%s(new)' % op)
|
|
except TypeError:
|
|
pass
|
|
else:
|
|
self._assert(False, 'Expected a TypeError, op: %s' % op)
|
|
|
|
def _test(self, func):
|
|
self.assertRaises(AttributeError, func, old)
|
|
self.assertRaises(TypeError, func, new)
|
|
|
|
def test_eq(self):
|
|
class A(object):
|
|
def __eq__(self, other):
|
|
return self.value == other.value
|
|
self.assertRaises(AttributeError, lambda: A() == A())
|
|
|
|
|
|
class GetAttrTestCase(unittest.TestCase):
|
|
def test_raising_custom_attribute_error(self):
|
|
# Very similar to
|
|
# test_descr_jy.TestDescrTestCase.test_raising_custom_attribute_error
|
|
class BarAttributeError(AttributeError): pass
|
|
|
|
class Bar(object):
|
|
def __getattr__(self, name):
|
|
raise BarAttributeError
|
|
|
|
class BarClassic:
|
|
def __getattr__(self, name):
|
|
raise BarAttributeError
|
|
|
|
class Foo(object):
|
|
def __getattr__(self, name):
|
|
raise AttributeError("Custom message")
|
|
|
|
class FooClassic:
|
|
def __getattr__(self, name):
|
|
raise AttributeError("Custom message")
|
|
|
|
self.assertRaises(BarAttributeError, lambda: Bar().x)
|
|
self.assertRaises(BarAttributeError, lambda: BarClassic().x)
|
|
|
|
try:
|
|
Foo().x
|
|
self.assert_(False) # Previous line should raise AttributteError
|
|
except AttributeError, e:
|
|
self.assertEquals("Custom message", str(e))
|
|
|
|
try:
|
|
FooClassic().x
|
|
self.assert_(False) # Previous line should raise AttributteError
|
|
except AttributeError, e:
|
|
self.assertEquals("Custom message", str(e))
|
|
|
|
|
|
class Base(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
|
|
def lookup_where(obj, name):
|
|
mro = type(obj).__mro__
|
|
for t in mro:
|
|
if name in t.__dict__:
|
|
return t.__dict__[name], t
|
|
return None, None
|
|
|
|
|
|
def refop(x, y, opname, ropname):
|
|
# this has been validated by running the tests on top of cpython
|
|
# so for the space of possibilities that the tests touch it is known
|
|
# to behave like cpython as long as the latter doesn't change its own
|
|
# algorithm
|
|
t1 = type(x)
|
|
t2 = type(y)
|
|
op, where1 = lookup_where(x, opname)
|
|
rop, where2 = lookup_where(y, ropname)
|
|
if op is None and rop is not None:
|
|
return rop(y, x)
|
|
if rop and where1 is not where2:
|
|
if (issubclass(t2, t1) and not issubclass(where1, where2) and
|
|
not issubclass(t1, where2)):
|
|
return rop(y, x)
|
|
if op is None:
|
|
return "TypeError"
|
|
return op(x,y)
|
|
|
|
|
|
def do_test(X, Y, name, impl):
|
|
x = X('x')
|
|
y = Y('y')
|
|
opname = '__%s__' % name
|
|
ropname = '__r%s__' % name
|
|
|
|
count = [0]
|
|
fail = []
|
|
|
|
def check(z1, z2):
|
|
ref = refop(z1, z2, opname, ropname)
|
|
try:
|
|
v = impl(z1, z2)
|
|
except TypeError:
|
|
v = "TypeError"
|
|
if v != ref:
|
|
fail.append(count[0])
|
|
|
|
def override_in_hier(n=6):
|
|
if n == 0:
|
|
count[0] += 1
|
|
check(x, y)
|
|
check(y, x)
|
|
return
|
|
|
|
f = lambda self, other: (n, self.name, other.name)
|
|
if n % 2 == 0:
|
|
name = opname
|
|
else:
|
|
name = ropname
|
|
|
|
for C in Y.__mro__:
|
|
if name in C.__dict__:
|
|
continue
|
|
if C is not object:
|
|
setattr(C, name, f)
|
|
override_in_hier(n - 1)
|
|
if C is not object:
|
|
delattr(C, name)
|
|
|
|
override_in_hier()
|
|
#print count[0]
|
|
return fail
|
|
|
|
|
|
class BinopCombinationsTestCase(unittest.TestCase):
|
|
|
|
"""Try to test more exhaustively binop overriding combination
|
|
cases"""
|
|
|
|
def test_binop_combinations_mul(self):
|
|
class X(Base):
|
|
pass
|
|
class Y(X):
|
|
pass
|
|
|
|
fail = do_test(X, Y, 'mul', lambda x, y: x*y)
|
|
#print len(fail)
|
|
self.assert_(not fail)
|
|
|
|
def test_binop_combinations_sub(self):
|
|
class X(Base):
|
|
pass
|
|
class Y(X):
|
|
pass
|
|
|
|
fail = do_test(X, Y, 'sub', lambda x, y: x-y)
|
|
#print len(fail)
|
|
self.assert_(not fail)
|
|
|
|
def test_binop_combinations_pow(self):
|
|
class X(Base):
|
|
pass
|
|
class Y(X):
|
|
pass
|
|
|
|
fail = do_test(X, Y, 'pow', lambda x, y: x**y)
|
|
#print len(fail)
|
|
self.assert_(not fail)
|
|
|
|
def test_binop_combinations_more_exhaustive(self):
|
|
class X(Base):
|
|
pass
|
|
|
|
class B1(object):
|
|
pass
|
|
|
|
class B2(object):
|
|
pass
|
|
|
|
class X1(B1, X, B2):
|
|
pass
|
|
|
|
class C1(object):
|
|
pass
|
|
|
|
class C2(object):
|
|
pass
|
|
|
|
class Y(C1, X1, C2):
|
|
pass
|
|
|
|
fail = do_test(X, Y, 'sub', lambda x, y: x - y)
|
|
#print len(fail)
|
|
self.assert_(not fail)
|
|
|
|
|
|
def test_main():
|
|
test_support.run_unittest(TestDescrTestCase,
|
|
SubclassDescrTestCase,
|
|
InPlaceTestCase,
|
|
DescrExceptionsTestCase,
|
|
GetAttrTestCase,
|
|
BinopCombinationsTestCase)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
test_main()
|