blob: e5cdf088f42ea75de1ea846b3f56765f2d4d2e46 [file] [log] [blame]
"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
@test_support.cpython_only
def testDelItem(self):
class A:
ok = False
def __delitem__(self, key):
self.ok = True
a = A()
# Subtle: we need to call PySequence_SetItem, not PyMapping_SetItem.
from _testcapi import sequence_delitem
sequence_delitem(a, 2)
self.assertTrue(a.ok)
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[:] = []
eval('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[:] = []
eval('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.assertEqual(["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.assertEqual(type(as_int), long)
self.assertEqual(as_int, 42L)
self.assertCallStack([('__int__', (mixIntAndLong,))])
callLst[:] = []
as_long = long(mixIntAndLong)
self.assertEqual(type(as_long), long)
self.assertEqual(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.assertEqual(a1.f, a1.f)
self.assertNotEqual(a1.f, a2.f)
self.assertNotEqual(a1.f, a1.g)
self.assertEqual(a1.f, A(1).f)
self.assertEqual(hash(a1.f), hash(a1.f))
self.assertEqual(hash(a1.f), hash(A(1).f))
self.assertNotEqual(A.f, a1.f)
self.assertNotEqual(A.f, A.g)
self.assertEqual(B.f, A.f)
self.assertEqual(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 testAttrSlots(self):
class C:
pass
for c in C, C():
self.assertRaises(TypeError, type(c).__getattribute__, c, [])
self.assertRaises(TypeError, type(c).__setattr__, c, [], [])
def test_main():
with test_support.check_py3k_warnings(
(".+__(get|set|del)slice__ has been removed", DeprecationWarning),
("classic int division", DeprecationWarning),
("<> not supported", DeprecationWarning)):
test_support.run_unittest(ClassTests)
if __name__=='__main__':
test_main()