799 lines
16 KiB
Plaintext
799 lines
16 KiB
Plaintext
|
# Test cases for (some) dunder methods (compile and run)
|
||
|
|
||
|
[case testDundersMisc]
|
||
|
# Legacy test case for dunders (don't add more here)
|
||
|
|
||
|
from typing import Any
|
||
|
class Item:
|
||
|
def __init__(self, value: str) -> None:
|
||
|
self.value = value
|
||
|
|
||
|
def __hash__(self) -> int:
|
||
|
return hash(self.value)
|
||
|
|
||
|
def __eq__(self, rhs: object) -> bool:
|
||
|
return isinstance(rhs, Item) and self.value == rhs.value
|
||
|
|
||
|
def __lt__(self, x: 'Item') -> bool:
|
||
|
return self.value < x.value
|
||
|
|
||
|
class Subclass1(Item):
|
||
|
def __bool__(self) -> bool:
|
||
|
return bool(self.value)
|
||
|
|
||
|
class NonBoxedThing:
|
||
|
def __getitem__(self, index: Item) -> Item:
|
||
|
return Item("2 * " + index.value + " + 1")
|
||
|
|
||
|
class BoxedThing:
|
||
|
def __getitem__(self, index: int) -> int:
|
||
|
return 2 * index + 1
|
||
|
|
||
|
class Subclass2(BoxedThing):
|
||
|
pass
|
||
|
|
||
|
class UsesNotImplemented:
|
||
|
def __eq__(self, b: object) -> bool:
|
||
|
return NotImplemented
|
||
|
|
||
|
def index_into(x : Any, y : Any) -> Any:
|
||
|
return x[y]
|
||
|
|
||
|
def internal_index_into() -> None:
|
||
|
x = BoxedThing()
|
||
|
print (x[3])
|
||
|
y = NonBoxedThing()
|
||
|
z = Item("3")
|
||
|
print(y[z].value)
|
||
|
|
||
|
def is_truthy(x: Item) -> bool:
|
||
|
return True if x else False
|
||
|
|
||
|
[file driver.py]
|
||
|
from native import *
|
||
|
x = BoxedThing()
|
||
|
y = 3
|
||
|
print(x[y], index_into(x, y))
|
||
|
|
||
|
x = Subclass2()
|
||
|
y = 3
|
||
|
print(x[y], index_into(x, y))
|
||
|
|
||
|
z = NonBoxedThing()
|
||
|
w = Item("3")
|
||
|
print(z[w].value, index_into(z, w).value)
|
||
|
|
||
|
i1 = Item('lolol')
|
||
|
i2 = Item('lol' + 'ol')
|
||
|
i3 = Item('xyzzy')
|
||
|
assert hash(i1) == hash(i2)
|
||
|
|
||
|
assert i1 == i2
|
||
|
assert not i1 != i2
|
||
|
assert not i1 == i3
|
||
|
assert i1 != i3
|
||
|
assert i2 < i3
|
||
|
assert not i1 < i2
|
||
|
assert i1 == Subclass1('lolol')
|
||
|
|
||
|
assert is_truthy(Item(''))
|
||
|
assert is_truthy(Item('a'))
|
||
|
assert not is_truthy(Subclass1(''))
|
||
|
assert is_truthy(Subclass1('a'))
|
||
|
|
||
|
assert UsesNotImplemented() != object()
|
||
|
|
||
|
internal_index_into()
|
||
|
[out]
|
||
|
7 7
|
||
|
7 7
|
||
|
2 * 3 + 1 2 * 3 + 1
|
||
|
7
|
||
|
2 * 3 + 1
|
||
|
|
||
|
[case testDundersContainer]
|
||
|
# Sequence/mapping dunder methods
|
||
|
|
||
|
from typing import Any
|
||
|
|
||
|
class Seq:
|
||
|
def __init__(self) -> None:
|
||
|
self.key = 0
|
||
|
self.value = 0
|
||
|
|
||
|
def __len__(self) -> int:
|
||
|
return 5
|
||
|
|
||
|
def __setitem__(self, key: int, value: int) -> None:
|
||
|
self.key = key
|
||
|
self.value = value
|
||
|
|
||
|
def __contains__(self, x: int) -> bool:
|
||
|
return x == 3
|
||
|
|
||
|
def __delitem__(self, key: int) -> None:
|
||
|
self.key = key
|
||
|
|
||
|
class Plain: pass
|
||
|
|
||
|
def any_seq() -> Any:
|
||
|
"""Return Any-typed Seq."""
|
||
|
return Seq()
|
||
|
|
||
|
def any_plain() -> Any:
|
||
|
"""Return Any-typed Seq."""
|
||
|
return Plain()
|
||
|
|
||
|
def test_len() -> None:
|
||
|
assert len(any_seq()) == 5
|
||
|
assert len(Seq()) == 5
|
||
|
|
||
|
def test_len_error() -> None:
|
||
|
try:
|
||
|
len(any_plain())
|
||
|
except TypeError:
|
||
|
pass
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
def test_set_item() -> None:
|
||
|
s = any_seq()
|
||
|
s[44] = 66
|
||
|
assert s.key == 44 and s.value == 66
|
||
|
ss = Seq()
|
||
|
ss[33] = 55
|
||
|
assert ss.key == 33 and ss.value == 55
|
||
|
|
||
|
def test_contains() -> None:
|
||
|
assert 3 in any_seq()
|
||
|
assert 4 not in any_seq()
|
||
|
assert 2 not in any_seq()
|
||
|
assert 3 in Seq()
|
||
|
assert 4 not in Seq()
|
||
|
assert 2 not in Seq()
|
||
|
|
||
|
def test_delitem() -> None:
|
||
|
s = any_seq()
|
||
|
del s[55]
|
||
|
assert s.key == 55
|
||
|
|
||
|
class SeqAny:
|
||
|
def __contains__(self, x: Any) -> Any:
|
||
|
return x == 3
|
||
|
|
||
|
def __setitem__(self, x: Any, y: Any) -> Any:
|
||
|
self.x = x
|
||
|
return 'x'
|
||
|
|
||
|
def test_contains_any() -> None:
|
||
|
assert (3 in SeqAny()) is True
|
||
|
assert (2 in SeqAny()) is False
|
||
|
assert (3 not in SeqAny()) is False
|
||
|
assert (2 not in SeqAny()) is True
|
||
|
s = SeqAny() # type: Any
|
||
|
assert (3 in s) is True
|
||
|
assert (2 in s) is False
|
||
|
assert (3 not in s) is False
|
||
|
assert (2 not in s) is True
|
||
|
|
||
|
def test_set_item_any() -> None:
|
||
|
s = SeqAny()
|
||
|
s[4] = 6
|
||
|
assert s.x == 4
|
||
|
ss = SeqAny() # type: Any
|
||
|
ss[5] = 7
|
||
|
assert ss.x == 5
|
||
|
|
||
|
class SeqError:
|
||
|
def __setitem__(self, key: int, value: int) -> None:
|
||
|
raise RuntimeError()
|
||
|
|
||
|
def __contains__(self, x: int) -> bool:
|
||
|
raise RuntimeError()
|
||
|
|
||
|
def __len__(self):
|
||
|
return -5
|
||
|
|
||
|
def any_seq_error() -> Any:
|
||
|
return SeqError()
|
||
|
|
||
|
def test_set_item_error_propagate() -> None:
|
||
|
s = any_seq_error()
|
||
|
try:
|
||
|
s[44] = 66
|
||
|
except RuntimeError:
|
||
|
pass
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
def test_contains_error_propagate() -> None:
|
||
|
s = any_seq_error()
|
||
|
try:
|
||
|
3 in s
|
||
|
except RuntimeError:
|
||
|
pass
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
def test_negative_len() -> None:
|
||
|
try:
|
||
|
len(SeqError())
|
||
|
except ValueError:
|
||
|
pass
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
class DelItemNoSetItem:
|
||
|
def __delitem__(self, x: int) -> None:
|
||
|
self.key = x
|
||
|
|
||
|
def test_del_item_with_no_set_item() -> None:
|
||
|
o = DelItemNoSetItem()
|
||
|
del o[22]
|
||
|
assert o.key == 22
|
||
|
a = o # type: Any
|
||
|
del a[12]
|
||
|
assert a.key == 12
|
||
|
try:
|
||
|
a[1] = 2
|
||
|
except TypeError as e:
|
||
|
assert str(e) == "'DelItemNoSetItem' object does not support item assignment"
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
class SetItemOverride(dict):
|
||
|
# Only override __setitem__, __delitem__ comes from dict
|
||
|
|
||
|
def __setitem__(self, x: int, y: int) -> None:
|
||
|
self.key = x
|
||
|
self.value = y
|
||
|
|
||
|
def test_set_item_override() -> None:
|
||
|
o = SetItemOverride({'x': 12, 'y': 13})
|
||
|
o[2] = 3
|
||
|
assert o.key == 2 and o.value == 3
|
||
|
a = o # type: Any
|
||
|
o[4] = 5
|
||
|
assert o.key == 4 and o.value == 5
|
||
|
assert o['x'] == 12
|
||
|
assert o['y'] == 13
|
||
|
del o['x']
|
||
|
assert 'x' not in o and 'y' in o
|
||
|
del a['y']
|
||
|
assert 'y' not in a and 'x' not in a
|
||
|
|
||
|
class DelItemOverride(dict):
|
||
|
# Only override __delitem__, __setitem__ comes from dict
|
||
|
|
||
|
def __delitem__(self, x: int) -> None:
|
||
|
self.key = x
|
||
|
|
||
|
def test_del_item_override() -> None:
|
||
|
o = DelItemOverride()
|
||
|
del o[2]
|
||
|
assert o.key == 2
|
||
|
a = o # type: Any
|
||
|
del o[5]
|
||
|
assert o.key == 5
|
||
|
o['x'] = 12
|
||
|
assert o['x'] == 12
|
||
|
a['y'] = 13
|
||
|
assert a['y'] == 13
|
||
|
|
||
|
class SetItemOverrideNative(Seq):
|
||
|
def __setitem__(self, key: int, value: int) -> None:
|
||
|
self.key = key + 1
|
||
|
self.value = value + 1
|
||
|
|
||
|
def test_native_set_item_override() -> None:
|
||
|
o = SetItemOverrideNative()
|
||
|
o[1] = 4
|
||
|
assert o.key == 2 and o.value == 5
|
||
|
del o[6]
|
||
|
assert o.key == 6
|
||
|
a = o # type: Any
|
||
|
a[10] = 12
|
||
|
assert a.key == 11 and a.value == 13
|
||
|
del a[16]
|
||
|
assert a.key == 16
|
||
|
|
||
|
class DelItemOverrideNative(Seq):
|
||
|
def __delitem__(self, key: int) -> None:
|
||
|
self.key = key + 2
|
||
|
|
||
|
def test_native_del_item_override() -> None:
|
||
|
o = DelItemOverrideNative()
|
||
|
o[1] = 4
|
||
|
assert o.key == 1 and o.value == 4
|
||
|
del o[6]
|
||
|
assert o.key == 8
|
||
|
a = o # type: Any
|
||
|
a[10] = 12
|
||
|
assert a.key == 10 and a.value == 12
|
||
|
del a[16]
|
||
|
assert a.key == 18
|
||
|
|
||
|
[case testDundersNumber]
|
||
|
from typing import Any
|
||
|
|
||
|
class C:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
|
||
|
def __neg__(self) -> int:
|
||
|
return self.x + 1
|
||
|
|
||
|
def __invert__(self) -> int:
|
||
|
return self.x + 2
|
||
|
|
||
|
def __int__(self) -> int:
|
||
|
return self.x + 3
|
||
|
|
||
|
def __float__(self) -> float:
|
||
|
return float(self.x + 4)
|
||
|
|
||
|
def test_unary_dunders_generic() -> None:
|
||
|
a: Any = C(10)
|
||
|
|
||
|
assert -a == 11
|
||
|
assert ~a == 12
|
||
|
assert int(a) == 13
|
||
|
assert float(a) == 14.0
|
||
|
|
||
|
def test_unary_dunders_native() -> None:
|
||
|
c = C(10)
|
||
|
|
||
|
assert -c == 11
|
||
|
assert ~c == 12
|
||
|
assert int(c) == 13
|
||
|
assert float(c) == 14.0
|
||
|
|
||
|
[case testDundersBinarySimple]
|
||
|
from typing import Any
|
||
|
|
||
|
class C:
|
||
|
def __init__(self) -> None:
|
||
|
self.x = 5
|
||
|
|
||
|
def __add__(self, y: int) -> int:
|
||
|
return self.x + y
|
||
|
|
||
|
def __sub__(self, y: int) -> int:
|
||
|
return self.x - y
|
||
|
|
||
|
def __mul__(self, y: int) -> int:
|
||
|
return self.x * y
|
||
|
|
||
|
def __mod__(self, y: int) -> int:
|
||
|
return self.x % y
|
||
|
|
||
|
def __lshift__(self, y: int) -> int:
|
||
|
return self.x << y
|
||
|
|
||
|
def __rshift__(self, y: int) -> int:
|
||
|
return self.x >> y
|
||
|
|
||
|
def __and__(self, y: int) -> int:
|
||
|
return self.x & y
|
||
|
|
||
|
def __or__(self, y: int) -> int:
|
||
|
return self.x | y
|
||
|
|
||
|
def __xor__(self, y: int) -> int:
|
||
|
return self.x ^ y
|
||
|
|
||
|
def __matmul__(self, y: int) -> int:
|
||
|
return self.x + y + 10
|
||
|
|
||
|
def __truediv__(self, y: int) -> int:
|
||
|
return self.x + y + 20
|
||
|
|
||
|
def __floordiv__(self, y: int) -> int:
|
||
|
return self.x + y + 30
|
||
|
|
||
|
def test_generic() -> None:
|
||
|
a: Any = C()
|
||
|
assert a + 3 == 8
|
||
|
assert a - 3 == 2
|
||
|
assert a * 5 == 25
|
||
|
assert a % 2 == 1
|
||
|
assert a << 4 == 80
|
||
|
assert a >> 0 == 5
|
||
|
assert a >> 1 == 2
|
||
|
assert a & 1 == 1
|
||
|
assert a | 3 == 7
|
||
|
assert a ^ 3 == 6
|
||
|
assert a @ 3 == 18
|
||
|
assert a / 2 == 27
|
||
|
assert a // 2 == 37
|
||
|
|
||
|
def test_native() -> None:
|
||
|
c = C()
|
||
|
assert c + 3 == 8
|
||
|
assert c - 3 == 2
|
||
|
|
||
|
def test_error() -> None:
|
||
|
a: Any = C()
|
||
|
try:
|
||
|
a + 'x'
|
||
|
except TypeError as e:
|
||
|
assert str(e) == "unsupported operand type(s) for +: 'C' and 'str'"
|
||
|
else:
|
||
|
assert False
|
||
|
try:
|
||
|
a - 'x'
|
||
|
except TypeError as e:
|
||
|
assert str(e) == "unsupported operand type(s) for -: 'C' and 'str'"
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
[case testDundersBinaryReverse]
|
||
|
from typing import Any
|
||
|
|
||
|
class C:
|
||
|
def __init__(self) -> None:
|
||
|
self.x = 5
|
||
|
|
||
|
def __add__(self, y: int) -> int:
|
||
|
return self.x + y
|
||
|
|
||
|
def __radd__(self, y: int) -> int:
|
||
|
return self.x + y + 1
|
||
|
|
||
|
def __sub__(self, y: int) -> int:
|
||
|
return self.x - y
|
||
|
|
||
|
def __rsub__(self, y: int) -> int:
|
||
|
return self.x - y - 1
|
||
|
|
||
|
def test_generic() -> None:
|
||
|
a: Any = C()
|
||
|
assert a + 3 == 8
|
||
|
assert 4 + a == 10
|
||
|
assert a - 3 == 2
|
||
|
assert 4 - a == 0
|
||
|
|
||
|
def test_native() -> None:
|
||
|
c = C()
|
||
|
assert c + 3 == 8
|
||
|
assert 4 + c == 10
|
||
|
assert c - 3 == 2
|
||
|
assert 4 - c == 0
|
||
|
|
||
|
def test_errors() -> None:
|
||
|
a: Any = C()
|
||
|
try:
|
||
|
a + 'x'
|
||
|
except TypeError as e:
|
||
|
assert str(e) == "unsupported operand type(s) for +: 'C' and 'str'"
|
||
|
else:
|
||
|
assert False
|
||
|
try:
|
||
|
a - 'x'
|
||
|
except TypeError as e:
|
||
|
assert str(e) == "unsupported operand type(s) for -: 'C' and 'str'"
|
||
|
else:
|
||
|
assert False
|
||
|
try:
|
||
|
'x' + a
|
||
|
except TypeError as e:
|
||
|
assert str(e) in ('can only concatenate str (not "C") to str',
|
||
|
'must be str, not C')
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
class F:
|
||
|
def __add__(self, x: int) -> int:
|
||
|
return 5
|
||
|
|
||
|
class G:
|
||
|
def __add__(self, x: int) -> int:
|
||
|
return 33
|
||
|
|
||
|
def __radd__(self, x: F) -> int:
|
||
|
return 6
|
||
|
|
||
|
def test_type_mismatch_fall_back_to_reverse() -> None:
|
||
|
assert F() + G() == 6
|
||
|
|
||
|
[case testDundersBinaryNotImplemented]
|
||
|
from typing import Any, Union
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
class C:
|
||
|
def __init__(self, v: int) -> None:
|
||
|
self.v = v
|
||
|
|
||
|
def __add__(self, y: int) -> Union[int, Any]:
|
||
|
if y == 1:
|
||
|
return self.v
|
||
|
return NotImplemented
|
||
|
|
||
|
def test_any_add() -> None:
|
||
|
a: Any = C(4)
|
||
|
assert a + 1 == 4
|
||
|
try:
|
||
|
a + 2
|
||
|
except TypeError:
|
||
|
pass
|
||
|
else:
|
||
|
assert False
|
||
|
|
||
|
class D:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
|
||
|
def __add__(self, e: E) -> Union[int, Any]:
|
||
|
if e.x == 1:
|
||
|
return 2
|
||
|
return NotImplemented
|
||
|
|
||
|
class E:
|
||
|
def __init__(self, x: int) -> None:
|
||
|
self.x = x
|
||
|
|
||
|
def __radd__(self, d: D) -> Union[int, Any]:
|
||
|
if d.x == 3:
|
||
|
return 4
|
||
|
return NotImplemented
|
||
|
|
||
|
def test_any_radd() -> None:
|
||
|
d1: Any = D(1)
|
||
|
d3: Any = D(3)
|
||
|
e1: Any = E(1)
|
||
|
e3: Any = E(3)
|
||
|
assert d1 + e1 == 2
|
||
|
assert d3 + e1 == 2
|
||
|
assert d3 + e3 == 4
|
||
|
|
||
|
class F:
|
||
|
def __init__(self, v):
|
||
|
self.v = v
|
||
|
|
||
|
def __add__(self, x):
|
||
|
if isinstance(x, int):
|
||
|
return self.v + x
|
||
|
return NotImplemented
|
||
|
|
||
|
class G:
|
||
|
def __radd__(self, x):
|
||
|
if isinstance(x, F):
|
||
|
return x.v + 1
|
||
|
if isinstance(x, str):
|
||
|
return 'a'
|
||
|
return NotImplemented
|
||
|
|
||
|
def test_unannotated_add() -> None:
|
||
|
o = F(4)
|
||
|
assert o + 5 == 9
|
||
|
with assertRaises(TypeError, "unsupported operand type(s) for +: 'F' and 'str'"):
|
||
|
o + 'x'
|
||
|
|
||
|
def test_unannotated_add_and_radd_1() -> None:
|
||
|
o = F(4)
|
||
|
assert o + G() == 5
|
||
|
|
||
|
def test_unannotated_radd() -> None:
|
||
|
assert 'x' + G() == 'a'
|
||
|
with assertRaises(TypeError, "unsupported operand type(s) for +: 'int' and 'G'"):
|
||
|
1 + G()
|
||
|
|
||
|
class H:
|
||
|
def __add__(self, x):
|
||
|
if isinstance(x, int):
|
||
|
return x + 1
|
||
|
return NotImplemented
|
||
|
|
||
|
def __radd__(self, x):
|
||
|
if isinstance(x, str):
|
||
|
return 22
|
||
|
return NotImplemented
|
||
|
|
||
|
def test_unannotated_add_and_radd_2() -> None:
|
||
|
h = H()
|
||
|
assert h + 5 == 6
|
||
|
assert 'x' + h == 22
|
||
|
with assertRaises(TypeError, "unsupported operand type(s) for +: 'int' and 'H'"):
|
||
|
1 + h
|
||
|
|
||
|
# TODO: Inheritance
|
||
|
|
||
|
[case testDifferentReverseDunders]
|
||
|
class C:
|
||
|
# __radd__ and __rsub__ are tested elsewhere
|
||
|
|
||
|
def __rmul__(self, x):
|
||
|
return 1
|
||
|
|
||
|
def __rtruediv__(self, x):
|
||
|
return 2
|
||
|
|
||
|
def __rmod__(self, x):
|
||
|
return 3
|
||
|
|
||
|
def __rfloordiv__(self, x):
|
||
|
return 4
|
||
|
|
||
|
def __rlshift__(self, x):
|
||
|
return 5
|
||
|
|
||
|
def __rrshift__(self, x):
|
||
|
return 6
|
||
|
|
||
|
def __rand__(self, x):
|
||
|
return 7
|
||
|
|
||
|
def __ror__(self, x):
|
||
|
return 8
|
||
|
|
||
|
def __rxor__(self, x):
|
||
|
return 9
|
||
|
|
||
|
def __rmatmul__(self, x):
|
||
|
return 10
|
||
|
|
||
|
def test_reverse_dunders() -> None:
|
||
|
x = 0
|
||
|
c = C()
|
||
|
assert x * c == 1
|
||
|
assert x / c == 2
|
||
|
assert x % c == 3
|
||
|
assert x // c == 4
|
||
|
assert x << c == 5
|
||
|
assert x >> c == 6
|
||
|
assert x & c == 7
|
||
|
assert x | c == 8
|
||
|
assert x ^ c == 9
|
||
|
assert x @ c == 10
|
||
|
|
||
|
[case testDundersInplace]
|
||
|
from typing import Any
|
||
|
from testutil import assertRaises
|
||
|
|
||
|
class C:
|
||
|
def __init__(self) -> None:
|
||
|
self.x = 5
|
||
|
|
||
|
def __iadd__(self, y: int) -> C:
|
||
|
self.x += y
|
||
|
return self
|
||
|
|
||
|
def __isub__(self, y: int) -> C:
|
||
|
self.x -= y
|
||
|
return self
|
||
|
|
||
|
def __imul__(self, y: int) -> C:
|
||
|
self.x *= y
|
||
|
return self
|
||
|
|
||
|
def __imod__(self, y: int) -> C:
|
||
|
self.x %= y
|
||
|
return self
|
||
|
|
||
|
def __itruediv__(self, y: int) -> C:
|
||
|
self.x += y + 10
|
||
|
return self
|
||
|
|
||
|
def __ifloordiv__(self, y: int) -> C:
|
||
|
self.x += y + 20
|
||
|
return self
|
||
|
|
||
|
def __ilshift__(self, y: int) -> C:
|
||
|
self.x <<= y
|
||
|
return self
|
||
|
|
||
|
def __irshift__(self, y: int) -> C:
|
||
|
self.x >>= y
|
||
|
return self
|
||
|
|
||
|
def __iand__(self, y: int) -> C:
|
||
|
self.x &= y
|
||
|
return self
|
||
|
|
||
|
def __ior__(self, y: int) -> C:
|
||
|
self.x |= y
|
||
|
return self
|
||
|
|
||
|
def __ixor__(self, y: int) -> C:
|
||
|
self.x ^= y
|
||
|
return self
|
||
|
|
||
|
def __imatmul__(self, y: int) -> C:
|
||
|
self.x += y + 5
|
||
|
return self
|
||
|
|
||
|
def test_generic_1() -> None:
|
||
|
c: Any = C()
|
||
|
c += 3
|
||
|
assert c.x == 8
|
||
|
c -= 5
|
||
|
assert c.x == 3
|
||
|
c *= 3
|
||
|
assert c.x == 9
|
||
|
c %= 4
|
||
|
assert c.x == 1
|
||
|
c /= 5
|
||
|
assert c.x == 16
|
||
|
c //= 4
|
||
|
assert c.x == 40
|
||
|
|
||
|
def test_generic_2() -> None:
|
||
|
c: Any = C()
|
||
|
c <<= 4
|
||
|
assert c.x == 80
|
||
|
c >>= 3
|
||
|
assert c.x == 10
|
||
|
c &= 3
|
||
|
assert c.x == 2
|
||
|
c |= 6
|
||
|
assert c.x == 6
|
||
|
c ^= 12
|
||
|
assert c.x == 10
|
||
|
c @= 3
|
||
|
assert c.x == 18
|
||
|
|
||
|
def test_native() -> None:
|
||
|
c = C()
|
||
|
c += 3
|
||
|
assert c.x == 8
|
||
|
c -= 5
|
||
|
assert c.x == 3
|
||
|
c *= 3
|
||
|
assert c.x == 9
|
||
|
|
||
|
def test_error() -> None:
|
||
|
c: Any = C()
|
||
|
with assertRaises(TypeError, "int object expected; got str"):
|
||
|
c += 'x'
|
||
|
|
||
|
class BadInplaceAdd:
|
||
|
def __init__(self):
|
||
|
self.x = 0
|
||
|
|
||
|
def __iadd__(self, x):
|
||
|
self.x += x
|
||
|
|
||
|
def test_in_place_operator_returns_none() -> None:
|
||
|
o = BadInplaceAdd()
|
||
|
with assertRaises(TypeError, "native.BadInplaceAdd object expected; got None"):
|
||
|
o += 5
|
||
|
|
||
|
[case testDunderMinMax]
|
||
|
class SomeItem:
|
||
|
def __init__(self, val: int) -> None:
|
||
|
self.val = val
|
||
|
|
||
|
def __lt__(self, x: 'SomeItem') -> bool:
|
||
|
return self.val < x.val
|
||
|
|
||
|
def __gt__(self, x: 'SomeItem') -> bool:
|
||
|
return self.val > x.val
|
||
|
|
||
|
class AnotherItem:
|
||
|
def __init__(self, val: str) -> None:
|
||
|
self.val = val
|
||
|
|
||
|
def __lt__(self, x: 'AnotherItem') -> bool:
|
||
|
return True
|
||
|
|
||
|
def __gt__(self, x: 'AnotherItem') -> bool:
|
||
|
return True
|
||
|
|
||
|
def test_dunder_min() -> None:
|
||
|
x = SomeItem(5)
|
||
|
y = SomeItem(10)
|
||
|
z = SomeItem(15)
|
||
|
assert min(x, y).val == 5
|
||
|
assert min(y, z).val == 10
|
||
|
assert max(x, y).val == 10
|
||
|
assert max(y, z).val == 15
|
||
|
x2 = AnotherItem('xxx')
|
||
|
y2 = AnotherItem('yyy')
|
||
|
z2 = AnotherItem('zzz')
|
||
|
assert min(x2, y2).val == 'yyy'
|
||
|
assert min(y2, x2).val == 'xxx'
|
||
|
assert max(x2, y2).val == 'yyy'
|
||
|
assert max(y2, x2).val == 'xxx'
|
||
|
assert min(y2, z2).val == 'zzz'
|
||
|
assert max(x2, z2).val == 'zzz'
|