| "Test the functionality of Python classes implementing operators." |
| |
| import unittest |
| |
| from test import test_support |
| |
| testmeths = [ |
| |
| # Binary operations |
| "add", |
| "radd", |
| "sub", |
| "rsub", |
| "mul", |
| "rmul", |
| "div", |
| "rdiv", |
| "mod", |
| "rmod", |
| "divmod", |
| "rdivmod", |
| "pow", |
| "rpow", |
| "rshift", |
| "rrshift", |
| "lshift", |
| "rlshift", |
| "and", |
| "rand", |
| "or", |
| "ror", |
| "xor", |
| "rxor", |
| |
| # List/dict operations |
| "contains", |
| "getitem", |
| "getslice", |
| "setitem", |
| "setslice", |
| "delitem", |
| "delslice", |
| |
| # Unary operations |
| "neg", |
| "pos", |
| "abs", |
| |
| # generic operations |
| "init", |
| ] |
| |
| # These need to return something other than None |
| # "coerce", |
| # "hash", |
| # "str", |
| # "repr", |
| # "int", |
| # "long", |
| # "float", |
| # "oct", |
| # "hex", |
| |
| # These are separate because they can influence the test of other methods. |
| # "getattr", |
| # "setattr", |
| # "delattr", |
| |
| callLst = [] |
| def trackCall(f): |
| def track(*args, **kwargs): |
| callLst.append((f.__name__, args)) |
| return f(*args, **kwargs) |
| return track |
| |
| class AllTests: |
| trackCall = trackCall |
| |
| @trackCall |
| def __coerce__(self, *args): |
| return (self,) + args |
| |
| @trackCall |
| def __hash__(self, *args): |
| return hash(id(self)) |
| |
| @trackCall |
| def __str__(self, *args): |
| return "AllTests" |
| |
| @trackCall |
| def __repr__(self, *args): |
| return "AllTests" |
| |
| @trackCall |
| def __int__(self, *args): |
| return 1 |
| |
| @trackCall |
| def __float__(self, *args): |
| return 1.0 |
| |
| @trackCall |
| def __long__(self, *args): |
| return 1L |
| |
| @trackCall |
| def __oct__(self, *args): |
| return '01' |
| |
| @trackCall |
| def __hex__(self, *args): |
| return '0x1' |
| |
| @trackCall |
| def __cmp__(self, *args): |
| return 0 |
| |
| # Synthesize all the other AllTests methods from the names in testmeths. |
| |
| method_template = """\ |
| @trackCall |
| def __%(method)s__(self, *args): |
| pass |
| """ |
| |
| for method in testmeths: |
| exec method_template % locals() in AllTests.__dict__ |
| |
| del method, method_template |
| |
| class ClassTests(unittest.TestCase): |
| def setUp(self): |
| callLst[:] = [] |
| |
| def assertCallStack(self, expected_calls): |
| actualCallList = callLst[:] # need to copy because the comparison below will add |
| # additional calls to callLst |
| if expected_calls != actualCallList: |
| self.fail("Expected call list:\n %s\ndoes not match actual call list\n %s" % |
| (expected_calls, actualCallList)) |
| |
| def testInit(self): |
| foo = AllTests() |
| self.assertCallStack([("__init__", (foo,))]) |
| |
| def testBinaryOps(self): |
| testme = AllTests() |
| # Binary operations |
| |
| callLst[:] = [] |
| testme + 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__add__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 + testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__radd__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme - 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__sub__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 - testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rsub__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme * 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__mul__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 * testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rmul__", (testme, 1))]) |
| |
| if 1/2 == 0: |
| callLst[:] = [] |
| testme / 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__div__", (testme, 1))]) |
| |
| |
| callLst[:] = [] |
| 1 / testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rdiv__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme % 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__mod__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 % testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rmod__", (testme, 1))]) |
| |
| |
| callLst[:] = [] |
| divmod(testme,1) |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__divmod__", (testme, 1))]) |
| |
| callLst[:] = [] |
| divmod(1, testme) |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rdivmod__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme ** 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__pow__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 ** testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rpow__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme >> 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rshift__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 >> testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rrshift__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme << 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__lshift__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 << testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rlshift__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme & 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__and__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 & testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rand__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme | 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__or__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 | testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__ror__", (testme, 1))]) |
| |
| callLst[:] = [] |
| testme ^ 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__xor__", (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 ^ testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ("__rxor__", (testme, 1))]) |
| |
| def testListAndDictOps(self): |
| testme = AllTests() |
| |
| # List/dict operations |
| |
| class Empty: pass |
| |
| try: |
| 1 in Empty() |
| self.fail('failed, should have raised TypeError') |
| except TypeError: |
| pass |
| |
| callLst[:] = [] |
| 1 in testme |
| self.assertCallStack([('__contains__', (testme, 1))]) |
| |
| callLst[:] = [] |
| testme[1] |
| self.assertCallStack([('__getitem__', (testme, 1))]) |
| |
| callLst[:] = [] |
| testme[1] = 1 |
| self.assertCallStack([('__setitem__', (testme, 1, 1))]) |
| |
| callLst[:] = [] |
| del testme[1] |
| self.assertCallStack([('__delitem__', (testme, 1))]) |
| |
| callLst[:] = [] |
| testme[:42] |
| self.assertCallStack([('__getslice__', (testme, 0, 42))]) |
| |
| callLst[:] = [] |
| testme[:42] = "The Answer" |
| self.assertCallStack([('__setslice__', (testme, 0, 42, "The Answer"))]) |
| |
| callLst[:] = [] |
| del testme[:42] |
| self.assertCallStack([('__delslice__', (testme, 0, 42))]) |
| |
| callLst[:] = [] |
| testme[2:1024:10] |
| self.assertCallStack([('__getitem__', (testme, slice(2, 1024, 10)))]) |
| |
| callLst[:] = [] |
| testme[2:1024:10] = "A lot" |
| self.assertCallStack([('__setitem__', (testme, slice(2, 1024, 10), |
| "A lot"))]) |
| callLst[:] = [] |
| del testme[2:1024:10] |
| self.assertCallStack([('__delitem__', (testme, slice(2, 1024, 10)))]) |
| |
| callLst[:] = [] |
| testme[:42, ..., :24:, 24, 100] |
| self.assertCallStack([('__getitem__', (testme, (slice(None, 42, None), |
| Ellipsis, |
| slice(None, 24, None), |
| 24, 100)))]) |
| callLst[:] = [] |
| testme[:42, ..., :24:, 24, 100] = "Strange" |
| self.assertCallStack([('__setitem__', (testme, (slice(None, 42, None), |
| Ellipsis, |
| slice(None, 24, None), |
| 24, 100), "Strange"))]) |
| callLst[:] = [] |
| del testme[:42, ..., :24:, 24, 100] |
| self.assertCallStack([('__delitem__', (testme, (slice(None, 42, None), |
| Ellipsis, |
| slice(None, 24, None), |
| 24, 100)))]) |
| |
| # Now remove the slice hooks to see if converting normal slices to |
| # slice object works. |
| |
| getslice = AllTests.__getslice__ |
| del AllTests.__getslice__ |
| setslice = AllTests.__setslice__ |
| del AllTests.__setslice__ |
| delslice = AllTests.__delslice__ |
| del AllTests.__delslice__ |
| |
| # XXX when using new-style classes the slice testme[:42] produces |
| # slice(None, 42, None) instead of slice(0, 42, None). py3k will have |
| # to change this test. |
| callLst[:] = [] |
| testme[:42] |
| self.assertCallStack([('__getitem__', (testme, slice(0, 42, None)))]) |
| |
| callLst[:] = [] |
| testme[:42] = "The Answer" |
| self.assertCallStack([('__setitem__', (testme, slice(0, 42, None), |
| "The Answer"))]) |
| callLst[:] = [] |
| del testme[:42] |
| self.assertCallStack([('__delitem__', (testme, slice(0, 42, None)))]) |
| |
| # Restore the slice methods, or the tests will fail with regrtest -R. |
| AllTests.__getslice__ = getslice |
| AllTests.__setslice__ = setslice |
| AllTests.__delslice__ = delslice |
| |
| |
| def testUnaryOps(self): |
| testme = AllTests() |
| |
| callLst[:] = [] |
| -testme |
| self.assertCallStack([('__neg__', (testme,))]) |
| callLst[:] = [] |
| +testme |
| self.assertCallStack([('__pos__', (testme,))]) |
| callLst[:] = [] |
| abs(testme) |
| self.assertCallStack([('__abs__', (testme,))]) |
| callLst[:] = [] |
| int(testme) |
| self.assertCallStack([('__int__', (testme,))]) |
| callLst[:] = [] |
| long(testme) |
| self.assertCallStack([('__long__', (testme,))]) |
| callLst[:] = [] |
| float(testme) |
| self.assertCallStack([('__float__', (testme,))]) |
| callLst[:] = [] |
| oct(testme) |
| self.assertCallStack([('__oct__', (testme,))]) |
| callLst[:] = [] |
| hex(testme) |
| self.assertCallStack([('__hex__', (testme,))]) |
| |
| |
| def testMisc(self): |
| testme = AllTests() |
| |
| callLst[:] = [] |
| hash(testme) |
| self.assertCallStack([('__hash__', (testme,))]) |
| |
| callLst[:] = [] |
| repr(testme) |
| self.assertCallStack([('__repr__', (testme,))]) |
| |
| callLst[:] = [] |
| str(testme) |
| self.assertCallStack([('__str__', (testme,))]) |
| |
| callLst[:] = [] |
| testme == 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
| |
| callLst[:] = [] |
| testme < 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
| |
| callLst[:] = [] |
| testme > 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
| |
| callLst[:] = [] |
| testme <> 1 # XXX kill this in py3k |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
| |
| callLst[:] = [] |
| testme != 1 |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) |
| |
| callLst[:] = [] |
| 1 == testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
| |
| callLst[:] = [] |
| 1 < testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
| |
| callLst[:] = [] |
| 1 > testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
| |
| callLst[:] = [] |
| 1 <> testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
| |
| callLst[:] = [] |
| 1 != testme |
| self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) |
| |
| |
| def testGetSetAndDel(self): |
| # Interfering tests |
| class ExtraTests(AllTests): |
| @trackCall |
| def __getattr__(self, *args): |
| return "SomeVal" |
| |
| @trackCall |
| def __setattr__(self, *args): |
| pass |
| |
| @trackCall |
| def __delattr__(self, *args): |
| pass |
| |
| testme = ExtraTests() |
| |
| callLst[:] = [] |
| testme.spam |
| self.assertCallStack([('__getattr__', (testme, "spam"))]) |
| |
| callLst[:] = [] |
| testme.eggs = "spam, spam, spam and ham" |
| self.assertCallStack([('__setattr__', (testme, "eggs", |
| "spam, spam, spam and ham"))]) |
| |
| callLst[:] = [] |
| del testme.cardinal |
| self.assertCallStack([('__delattr__', (testme, "cardinal"))]) |
| |
| def testDel(self): |
| x = [] |
| |
| class DelTest: |
| def __del__(self): |
| x.append("crab people, crab people") |
| testme = DelTest() |
| del testme |
| import gc |
| gc.collect() |
| self.assertEquals(["crab people, crab people"], x) |
| |
| def testBadTypeReturned(self): |
| # return values of some method are type-checked |
| class BadTypeClass: |
| def __int__(self): |
| return None |
| __float__ = __int__ |
| __long__ = __int__ |
| __str__ = __int__ |
| __repr__ = __int__ |
| __oct__ = __int__ |
| __hex__ = __int__ |
| |
| for f in [int, float, long, str, repr, oct, hex]: |
| self.assertRaises(TypeError, f, BadTypeClass()) |
| |
| def testMixIntsAndLongs(self): |
| # mixing up ints and longs is okay |
| class IntLongMixClass: |
| @trackCall |
| def __int__(self): |
| return 42L |
| |
| @trackCall |
| def __long__(self): |
| return 64 |
| |
| mixIntAndLong = IntLongMixClass() |
| |
| callLst[:] = [] |
| as_int = int(mixIntAndLong) |
| self.assertEquals(type(as_int), long) |
| self.assertEquals(as_int, 42L) |
| self.assertCallStack([('__int__', (mixIntAndLong,))]) |
| |
| callLst[:] = [] |
| as_long = long(mixIntAndLong) |
| self.assertEquals(type(as_long), int) |
| self.assertEquals(as_long, 64) |
| self.assertCallStack([('__long__', (mixIntAndLong,))]) |
| |
| def testHashStuff(self): |
| # Test correct errors from hash() on objects with comparisons but |
| # no __hash__ |
| |
| class C0: |
| pass |
| |
| hash(C0()) # This should work; the next two should raise TypeError |
| |
| class C1: |
| def __cmp__(self, other): return 0 |
| |
| self.assertRaises(TypeError, hash, C1()) |
| |
| class C2: |
| def __eq__(self, other): return 1 |
| |
| self.assertRaises(TypeError, hash, C2()) |
| |
| |
| def testSFBug532646(self): |
| # Test for SF bug 532646 |
| |
| class A: |
| pass |
| A.__call__ = A() |
| a = A() |
| |
| try: |
| a() # This should not segfault |
| except RuntimeError: |
| pass |
| else: |
| self.fail("Failed to raise RuntimeError") |
| |
| def testForExceptionsRaisedInInstanceGetattr2(self): |
| # Tests for exceptions raised in instance_getattr2(). |
| |
| def booh(self): |
| raise AttributeError("booh") |
| |
| class A: |
| a = property(booh) |
| try: |
| A().a # Raised AttributeError: A instance has no attribute 'a' |
| except AttributeError, x: |
| if str(x) != "booh": |
| self.fail("attribute error for A().a got masked: %s" % x) |
| |
| class E: |
| __eq__ = property(booh) |
| E() == E() # In debug mode, caused a C-level assert() to fail |
| |
| class I: |
| __init__ = property(booh) |
| try: |
| # In debug mode, printed XXX undetected error and |
| # raises AttributeError |
| I() |
| except AttributeError, x: |
| pass |
| else: |
| self.fail("attribute error for I.__init__ got masked") |
| |
| def testHashComparisonOfMethods(self): |
| # Test comparison and hash of methods |
| class A: |
| def __init__(self, x): |
| self.x = x |
| def f(self): |
| pass |
| def g(self): |
| pass |
| def __eq__(self, other): |
| return self.x == other.x |
| def __hash__(self): |
| return self.x |
| class B(A): |
| pass |
| |
| a1 = A(1) |
| a2 = A(2) |
| self.assertEquals(a1.f, a1.f) |
| self.assertNotEquals(a1.f, a2.f) |
| self.assertNotEquals(a1.f, a1.g) |
| self.assertEquals(a1.f, A(1).f) |
| self.assertEquals(hash(a1.f), hash(a1.f)) |
| self.assertEquals(hash(a1.f), hash(A(1).f)) |
| |
| self.assertNotEquals(A.f, a1.f) |
| self.assertNotEquals(A.f, A.g) |
| self.assertEquals(B.f, A.f) |
| self.assertEquals(hash(B.f), hash(A.f)) |
| |
| # the following triggers a SystemError in 2.4 |
| a = A(hash(A.f.im_func)^(-1)) |
| hash(a.f) |
| |
| def test_main(): |
| test_support.run_unittest(ClassTests) |
| |
| if __name__=='__main__': |
| test_main() |