400 lines
7.0 KiB
Python
400 lines
7.0 KiB
Python
"Test the functionality of Python classes implementing operators."
|
|
|
|
from test.test_support import TestFailed
|
|
|
|
testmeths = [
|
|
|
|
# Binary operations
|
|
"add",
|
|
"radd",
|
|
"sub",
|
|
"rsub",
|
|
"mul",
|
|
"rmul",
|
|
"truediv",
|
|
"rtruediv",
|
|
"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
|
|
# "hash",
|
|
# "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 __float__(self, *args):
|
|
print("__float__:", args)
|
|
return 1.0
|
|
|
|
def __oct__(self, *args):
|
|
print("__oct__:", args)
|
|
return '01'
|
|
|
|
def __hex__(self, *args):
|
|
print("__hex__:", args)
|
|
return '0x1'
|
|
|
|
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)
|
|
|
|
# Synthesize AllTests methods from the names in testmeths.
|
|
|
|
method_template = """\
|
|
def __%(method)s__(self, *args):
|
|
print("__%(method)s__:", args)
|
|
"""
|
|
|
|
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
|
|
|
|
divmod(testme,1)
|
|
divmod(1, testme)
|
|
|
|
testme ** 1
|
|
1 ** testme
|
|
|
|
testme >> 1
|
|
1 >> testme
|
|
|
|
testme << 1
|
|
1 << testme
|
|
|
|
testme & 1
|
|
1 & testme
|
|
|
|
testme | 1
|
|
1 | testme
|
|
|
|
testme ^ 1
|
|
1 ^ testme
|
|
|
|
|
|
# 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]
|
|
|
|
testme[2:1024:10]
|
|
testme[2:1024:10] = "A lot"
|
|
del testme[2:1024:10]
|
|
|
|
testme[:42, ..., :24:, 24, 100]
|
|
testme[:42, ..., :24:, 24, 100] = "Strange"
|
|
del testme[:42, ..., :24:, 24, 100]
|
|
|
|
|
|
# Now remove the slice hooks to see if converting normal slices to slice
|
|
# object works.
|
|
|
|
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)
|
|
hex(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__
|
|
__oct__ = __int__
|
|
__hex__ = __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
|
|
|
|
hash(C0()) # This should work; the next two should raise TypeError
|
|
|
|
class C1:
|
|
def __cmp__(self, other): return 0
|
|
|
|
check_exc("hash(C1())", TypeError)
|
|
|
|
class C2:
|
|
def __eq__(self, other): return 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
|
|
|
|
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)
|