diff options
Diffstat (limited to 'Lib/test/test_class.py')
-rw-r--r-- | Lib/test/test_class.py | 811 |
1 files changed, 507 insertions, 304 deletions
diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 003b4a51e6a..bde63a8045f 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -1,6 +1,9 @@ "Test the functionality of Python classes implementing operators." -from test.test_support import TestFailed +import unittest +import sys + +from test import test_support testmeths = [ @@ -53,340 +56,540 @@ testmeths = [ # "str", # "repr", # "int", -# "long", # "float", -# "oct", -# "hex", # These are separate because they can influence the test of other methods. # "getattr", # "setattr", # "delattr", -class AllTests: - def __hash__(self, *args): - print("__hash__:", args) - return hash(id(self)) - - def __str__(self, *args): - print("__str__:", args) - return "AllTests" - - def __repr__(self, *args): - print("__repr__:", args) - return "AllTests" - - def __int__(self, *args): - print("__int__:", args) - return 1 - - def __index__(self, *args): - print("__index__:", args) - return 1 - - def __float__(self, *args): - print("__float__:", args) - return 1.0 - - def __cmp__(self, *args): - print("__cmp__:", args) - return 0 - - def __eq__(self, *args): - print("__eq__:", args) - return True - - def __ne__(self, *args): - print("__ne__:", args) - return False - - def __lt__(self, *args): - print("__lt__:", args) - return False - - def __le__(self, *args): - print("__le__:", args) - return True - - def __gt__(self, *args): - print("__gt__:", args) - return False - - def __ge__(self, *args): - print("__ge__:", args) - return True - - def __del__(self, *args): - print("__del__:", args) +callLst = [] +def trackCall(f): + def track(*args, **kwargs): + callLst.append((f.__name__, args)) + return f(*args, **kwargs) + return track + +statictests = """ +@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 __index__(self, *args): + return 1 + +@trackCall +def __float__(self, *args): + return 1.0 + +@trackCall +def __cmp__(self, *args): + return 0 + +@trackCall +def __eq__(self, *args): + return True + +@trackCall +def __ne__(self, *args): + return False + +@trackCall +def __lt__(self, *args): + return False + +@trackCall +def __le__(self, *args): + return True + +@trackCall +def __gt__(self, *args): + return False + +@trackCall +def __ge__(self, *args): + return True +""" -# Synthesize AllTests methods from the names in testmeths. +# Synthesize all the other AllTests methods from the names in testmeths. method_template = """\ -def __%(method)s__(self, *args): - print("__%(method)s__:", args) +@trackCall +def __%s__(self, *args): + pass """ d = {} +exec(statictests, globals(), d) for method in testmeths: - exec(method_template % locals(), d) -for k in d: - setattr(AllTests, k, d[k]) -del d, k -del method, method_template - -# this also tests __init__ of course. -testme = AllTests() - -# Binary operations - -testme + 1 -1 + testme - -testme - 1 -1 - testme - -testme * 1 -1 * testme - -testme / 1 -1 / testme - -testme % 1 -1 % testme + exec(method_template % method, globals(), d) +AllTests = type("AllTests", (object,), d) +del d, statictests, method, method_template -divmod(testme,1) -divmod(1, testme) +class ClassTests(unittest.TestCase): + def setUp(self): + callLst[:] = [] -testme ** 1 -1 ** testme + 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)) -testme >> 1 -1 >> testme + def testInit(self): + foo = AllTests() + self.assertCallStack([("__init__", (foo,))]) -testme << 1 -1 << testme + def testBinaryOps(self): + testme = AllTests() + # Binary operations -testme & 1 -1 & testme + callLst[:] = [] + testme + 1 + self.assertCallStack([("__add__", (testme, 1))]) -testme | 1 -1 | testme + callLst[:] = [] + 1 + testme + self.assertCallStack([("__radd__", (testme, 1))]) -testme ^ 1 -1 ^ testme + callLst[:] = [] + testme - 1 + self.assertCallStack([("__sub__", (testme, 1))]) + callLst[:] = [] + 1 - testme + self.assertCallStack([("__rsub__", (testme, 1))]) -# List/dict operations - -class Empty: pass - -try: - 1 in Empty() - print('failed, should have raised TypeError') -except TypeError: - pass - -1 in testme - -testme[1] -testme[1] = 1 -del testme[1] - -testme[:42] -testme[:42] = "The Answer" -del testme[:42] + callLst[:] = [] + testme * 1 + self.assertCallStack([("__mul__", (testme, 1))]) -testme[2:1024:10] -testme[2:1024:10] = "A lot" -del testme[2:1024:10] + callLst[:] = [] + 1 * testme + self.assertCallStack([("__rmul__", (testme, 1))]) -testme[:42, ..., :24:, 24, 100] -testme[:42, ..., :24:, 24, 100] = "Strange" -del testme[:42, ..., :24:, 24, 100] + if 1/2 == 0: + callLst[:] = [] + testme / 1 + self.assertCallStack([("__div__", (testme, 1))]) -# Now remove the slice hooks to see if converting normal slices to slice -# object works. + callLst[:] = [] + 1 / testme + self.assertCallStack([("__rdiv__", (testme, 1))]) -del AllTests.__getslice__ -del AllTests.__setslice__ -del AllTests.__delslice__ - -import sys -if sys.platform[:4] != 'java': - testme[:42] - testme[:42] = "The Answer" - del testme[:42] -else: - # This works under Jython, but the actual slice values are - # different. - print("__getitem__: (slice(0, 42, None),)") - print("__setitem__: (slice(0, 42, None), 'The Answer')") - print("__delitem__: (slice(0, 42, None),)") - -# Unary operations - --testme -+testme -abs(testme) -int(testme) -int(testme) -float(testme) -oct(testme) - -# And the rest... - -hash(testme) -repr(testme) -str(testme) - -testme == 1 -testme < 1 -testme > 1 -testme != 1 -1 == testme -1 < testme -1 > testme -1 != testme - -# This test has to be last (duh.) - -del testme -if sys.platform[:4] == 'java': - import java - java.lang.System.gc() - -# Interfering tests - -class ExtraTests: - def __getattr__(self, *args): - print("__getattr__:", args) - return "SomeVal" - - def __setattr__(self, *args): - print("__setattr__:", args) - - def __delattr__(self, *args): - print("__delattr__:", args) - -testme = ExtraTests() -testme.spam -testme.eggs = "spam, spam, spam and ham" -del testme.cardinal - - -# return values of some method are type-checked -class BadTypeClass: - def __int__(self): - return None - __float__ = __int__ - __str__ = __int__ - __repr__ = __int__ - -def check_exc(stmt, exception): - """Raise TestFailed if executing 'stmt' does not raise 'exception' - """ - try: - exec(stmt) - except exception: - pass - else: - raise TestFailed, "%s should raise %s" % (stmt, exception) - -check_exc("int(BadTypeClass())", TypeError) -check_exc("float(BadTypeClass())", TypeError) -check_exc("str(BadTypeClass())", TypeError) -check_exc("repr(BadTypeClass())", TypeError) -check_exc("oct(BadTypeClass())", TypeError) -check_exc("hex(BadTypeClass())", TypeError) - -# Test correct errors from hash() on objects with comparisons but no __hash__ - -class C0: - pass + callLst[:] = [] + testme % 1 + self.assertCallStack([("__mod__", (testme, 1))]) -hash(C0()) # This should work; the next two should raise TypeError + callLst[:] = [] + 1 % testme + self.assertCallStack([("__rmod__", (testme, 1))]) -class C1: - def __cmp__(self, other): return 0 -check_exc("hash(C1())", TypeError) + callLst[:] = [] + divmod(testme,1) + self.assertCallStack([("__divmod__", (testme, 1))]) -class C2: - def __eq__(self, other): return 1 + callLst[:] = [] + divmod(1, testme) + self.assertCallStack([("__rdivmod__", (testme, 1))]) -check_exc("hash(C2())", TypeError) - -# Test for SF bug 532646 - -class A: - pass -A.__call__ = A() -a = A() -try: - a() # This should not segfault -except RuntimeError: - pass -else: - raise TestFailed, "how could this not have overflowed the stack?" - - -# 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 as x: - if str(x) != "booh": - print("attribute error for A().a got masked:", str(x)) - -class E: - __eq__ = property(booh) -E() == E() # In debug mode, caused a C-level assert() to fail - -class I: - __init__ = property(booh) -try: - I() # In debug mode, printed XXX undetected error and raises AttributeError -except AttributeError as x: - pass -else: - print("attribute error for I.__init__ got masked") - - -# 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 + callLst[:] = [] + testme ** 1 + self.assertCallStack([("__pow__", (testme, 1))]) -a1 = A(1) -a2 = A(2) -assert a1.f == a1.f -assert a1.f != a2.f -assert a1.f != a1.g -assert a1.f == A(1).f -assert hash(a1.f) == hash(a1.f) -assert hash(a1.f) == hash(A(1).f) - -assert A.f != a1.f -assert A.f != A.g -assert B.f == A.f -assert 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) + callLst[:] = [] + 1 ** testme + self.assertCallStack([("__rpow__", (testme, 1))]) + + callLst[:] = [] + testme >> 1 + self.assertCallStack([("__rshift__", (testme, 1))]) + + callLst[:] = [] + 1 >> testme + self.assertCallStack([("__rrshift__", (testme, 1))]) + + callLst[:] = [] + testme << 1 + self.assertCallStack([("__lshift__", (testme, 1))]) + + callLst[:] = [] + 1 << testme + self.assertCallStack([("__rlshift__", (testme, 1))]) + + callLst[:] = [] + testme & 1 + self.assertCallStack([("__and__", (testme, 1))]) + + callLst[:] = [] + 1 & testme + self.assertCallStack([("__rand__", (testme, 1))]) + + callLst[:] = [] + testme | 1 + self.assertCallStack([("__or__", (testme, 1))]) + + callLst[:] = [] + 1 | testme + self.assertCallStack([("__ror__", (testme, 1))]) + + callLst[:] = [] + testme ^ 1 + self.assertCallStack([("__xor__", (testme, 1))]) + + callLst[:] = [] + 1 ^ testme + self.assertCallStack([("__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[0:42] + self.assertCallStack([('__getitem__', (testme, slice(0, 42, None)))]) + + callLst[:] = [] + testme[:42] = "The Answer" + self.assertCallStack([('__setitem__', (testme, slice(None, 42, None), + "The Answer"))]) + callLst[:] = [] + del testme[0: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[:] = [] + float(testme) + self.assertCallStack([('__float__', (testme,))]) + callLst[:] = [] + oct(testme) + self.assertCallStack([('__index__', (testme,))]) + callLst[:] = [] + hex(testme) + self.assertCallStack([('__index__', (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([('__eq__', (testme, 1))]) + + callLst[:] = [] + testme < 1 + self.assertCallStack([('__lt__', (testme, 1))]) + + callLst[:] = [] + testme > 1 + self.assertCallStack([('__gt__', (testme, 1))]) + + callLst[:] = [] + testme != 1 + self.assertCallStack([('__ne__', (testme, 1))]) + + callLst[:] = [] + 1 == testme + self.assertCallStack([('__eq__', (1, testme))]) + + callLst[:] = [] + 1 < testme + self.assertCallStack([('__gt__', (1, testme))]) + + callLst[:] = [] + 1 > testme + self.assertCallStack([('__lt__', (1, testme))]) + + callLst[:] = [] + 1 != testme + self.assertCallStack([('__ne__', (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__ + __str__ = __int__ + __repr__ = __int__ + __oct__ = __int__ + __hex__ = __int__ + + for f in [int, float, str, repr, oct, hex]: + self.assertRaises(TypeError, f, BadTypeClass()) + + 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 as 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 as 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() |