mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
305 lines
8.6 KiB
Python
305 lines
8.6 KiB
Python
from operator import iadd, imul, mul
|
|
|
|
from testutils import assert_raises
|
|
|
|
assert -3 // 2 == -2
|
|
assert -3 % 2 == 1
|
|
|
|
a = 4
|
|
|
|
assert a**3 == 64
|
|
assert a * 3 == 12
|
|
assert a / 2 == 2
|
|
assert 2 == a / 2
|
|
assert a % 3 == 1
|
|
assert a - 3 == 1
|
|
assert -a == -4
|
|
assert +a == 4
|
|
|
|
# bitwise
|
|
|
|
assert 8 >> 3 == 1
|
|
assert 8 << 3 == 64
|
|
|
|
# Left shift raises type error
|
|
assert_raises(TypeError, lambda: 1 << 0.1)
|
|
assert_raises(TypeError, lambda: 1 << "abc")
|
|
|
|
# Right shift raises type error
|
|
assert_raises(TypeError, lambda: 1 >> 0.1)
|
|
assert_raises(TypeError, lambda: 1 >> "abc")
|
|
|
|
# Left shift raises value error on negative
|
|
assert_raises(ValueError, lambda: 1 << -1)
|
|
|
|
# Right shift raises value error on negative
|
|
assert_raises(ValueError, lambda: 1 >> -1)
|
|
|
|
# Bitwise or, and, xor raises value error on incompatible types
|
|
assert_raises(TypeError, lambda: "abc" | True)
|
|
assert_raises(TypeError, lambda: "abc" & True)
|
|
assert_raises(TypeError, lambda: "abc" ^ True)
|
|
assert_raises(TypeError, lambda: True | "abc")
|
|
assert_raises(TypeError, lambda: True & "abc")
|
|
assert_raises(TypeError, lambda: True ^ "abc")
|
|
assert_raises(TypeError, lambda: "abc" | 1.5)
|
|
assert_raises(TypeError, lambda: "abc" & 1.5)
|
|
assert_raises(TypeError, lambda: "abc" ^ 1.5)
|
|
assert_raises(TypeError, lambda: 1.5 | "abc")
|
|
assert_raises(TypeError, lambda: 1.5 & "abc")
|
|
assert_raises(TypeError, lambda: 1.5 ^ "abc")
|
|
assert_raises(TypeError, lambda: True | 1.5)
|
|
assert_raises(TypeError, lambda: True & 1.5)
|
|
assert_raises(TypeError, lambda: True ^ 1.5)
|
|
assert_raises(TypeError, lambda: 1.5 | True)
|
|
assert_raises(TypeError, lambda: 1.5 & True)
|
|
assert_raises(TypeError, lambda: 1.5 ^ True)
|
|
|
|
|
|
def check_repeat_error(error, message, operation, *args):
|
|
with assert_raises(error) as caught:
|
|
operation(*args)
|
|
assert str(caught.exception) == message, str(caught.exception)
|
|
|
|
|
|
class GetItemOnly:
|
|
def __getitem__(self, index):
|
|
raise IndexError
|
|
|
|
|
|
# An item slot does not imply support for repetition.
|
|
for value in (range(3), GetItemOnly()):
|
|
name = type(value).__name__
|
|
for operation, symbol in ((mul, "*"), (imul, "*=")):
|
|
check_repeat_error(
|
|
TypeError,
|
|
f"unsupported operand type(s) for {symbol}: '{name}' and 'int'",
|
|
operation,
|
|
value,
|
|
2,
|
|
)
|
|
check_repeat_error(
|
|
TypeError,
|
|
f"unsupported operand type(s) for *: 'int' and '{name}'",
|
|
mul,
|
|
2,
|
|
value,
|
|
)
|
|
|
|
|
|
# Native sequence slots use the same count conversion in both directions.
|
|
for value in ([1], (1,), "a", b"a", bytearray(b"a")):
|
|
namespace = {}
|
|
exec("def reverse(seq, count):\n return count * seq", namespace)
|
|
for operation_index, operation in enumerate((mul, namespace["reverse"], imul)):
|
|
for _ in range(64):
|
|
operation(value, 1)
|
|
for count, error, message in (
|
|
(1.5, TypeError, "can't multiply sequence by non-int of type 'float'"),
|
|
(2**100, OverflowError, "cannot fit 'int' into an index-sized integer"),
|
|
):
|
|
if (
|
|
type(value) in (str, bytes, tuple)
|
|
and operation_index == 1
|
|
and error is OverflowError
|
|
):
|
|
message = "Python int too large to convert to C ssize_t"
|
|
check_repeat_error(error, message, operation, value, count)
|
|
|
|
|
|
class RepeatIndex:
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __index__(self):
|
|
return self.value
|
|
|
|
|
|
assert RepeatIndex(2) * [1] == [1, 1]
|
|
assert imul(2, [1]) == [1, 1]
|
|
check_repeat_error(
|
|
TypeError,
|
|
"unsupported operand type(s) for *=: 'RepeatIndex' and 'list'",
|
|
imul,
|
|
RepeatIndex(2),
|
|
[1],
|
|
)
|
|
check_repeat_error(
|
|
OverflowError,
|
|
"cannot fit 'RepeatIndex' into an index-sized integer",
|
|
mul,
|
|
"a",
|
|
RepeatIndex(-(2**100)),
|
|
)
|
|
|
|
|
|
# Each expression has fresh bytecode so cold and warmed paths are both checked.
|
|
for value in ("a", b"a", (1,), "", b"", ()):
|
|
for expression in (
|
|
"return seq * count",
|
|
"return count * seq",
|
|
"seq *= count; return seq",
|
|
"count *= seq; return count",
|
|
):
|
|
namespace = {}
|
|
exec("def repeat(seq, count):\n " + expression, namespace)
|
|
repeat = namespace["repeat"]
|
|
check_repeat_error(
|
|
OverflowError,
|
|
"cannot fit 'int' into an index-sized integer",
|
|
repeat,
|
|
value,
|
|
2**100,
|
|
)
|
|
for _ in range(64):
|
|
assert repeat(value, 2) == mul(value, 2)
|
|
assert repeat(value, -2) == value[:0]
|
|
assert repeat(value, 0) == value[:0]
|
|
assert repeat(value, 1) == value
|
|
for count in (2**100, -(2**100)):
|
|
check_repeat_error(
|
|
OverflowError,
|
|
"Python int too large to convert to C ssize_t",
|
|
repeat,
|
|
value,
|
|
count,
|
|
)
|
|
for other in ("a", b"a", (1,)):
|
|
if type(other) is not type(value):
|
|
check_repeat_error(
|
|
OverflowError,
|
|
"cannot fit 'int' into an index-sized integer",
|
|
repeat,
|
|
other,
|
|
2**100,
|
|
)
|
|
if expression == "count *= seq; return count":
|
|
check_repeat_error(
|
|
TypeError,
|
|
f"unsupported operand type(s) for *=: 'RepeatIndex' and '{type(value).__name__}'",
|
|
repeat,
|
|
value,
|
|
RepeatIndex(2),
|
|
)
|
|
else:
|
|
assert repeat(value, RepeatIndex(2)) == mul(value, 2)
|
|
check_repeat_error(
|
|
OverflowError,
|
|
"cannot fit 'RepeatIndex' into an index-sized integer",
|
|
repeat,
|
|
value,
|
|
RepeatIndex(2**100),
|
|
)
|
|
subclass = type("RepeatSubclass", (type(value),), {})(value)
|
|
assert repeat(subclass, 2) == mul(value, 2)
|
|
overridden = type(
|
|
"OverrideRepeat",
|
|
(type(value),),
|
|
{
|
|
"__mul__": lambda self, count: "forward",
|
|
"__rmul__": lambda self, count: "reverse",
|
|
},
|
|
)(value)
|
|
expected = (
|
|
"forward" if expression.startswith(("return seq", "seq *=")) else "reverse"
|
|
)
|
|
assert repeat(overridden, 2) == expected
|
|
|
|
|
|
class DeclineRepeat(GetItemOnly):
|
|
calls = []
|
|
|
|
def __mul__(self, other):
|
|
self.calls.append("mul")
|
|
return NotImplemented
|
|
|
|
def __rmul__(self, other):
|
|
self.calls.append("rmul")
|
|
return NotImplemented
|
|
|
|
def __imul__(self, other):
|
|
self.calls.append("imul")
|
|
return NotImplemented
|
|
|
|
|
|
# Numeric fallback must not invoke the same Python method through a sequence slot.
|
|
declining = DeclineRepeat()
|
|
for operation, args, expected in (
|
|
(mul, (declining, 2), ["mul"]),
|
|
(mul, (2, declining), ["rmul"]),
|
|
(imul, (declining, 2), ["imul", "mul"]),
|
|
):
|
|
DeclineRepeat.calls.clear()
|
|
assert_raises(TypeError, operation, *args)
|
|
assert DeclineRepeat.calls == expected
|
|
|
|
|
|
class RepeatList(list):
|
|
pass
|
|
|
|
|
|
class RepeatInt(int):
|
|
pass
|
|
|
|
|
|
assert "a" * RepeatInt(2) == RepeatInt(2) * "a" == "aa"
|
|
for method, operation in (
|
|
("__mul__", lambda value: value * 2),
|
|
("__rmul__", lambda value: 2 * value),
|
|
):
|
|
value = RepeatList([1])
|
|
assert operation(value) == [1, 1]
|
|
setattr(RepeatList, method, lambda self, other: NotImplemented)
|
|
assert_raises(TypeError, operation, value)
|
|
delattr(RepeatList, method)
|
|
assert operation(value) == [1, 1]
|
|
|
|
|
|
# Inherited sequence slots must leave reflected numeric dispatch its priority.
|
|
class ReflectedCount(int):
|
|
def __rmul__(self, value):
|
|
return "reflected"
|
|
|
|
|
|
class InheritedRepeat(list):
|
|
pass
|
|
|
|
|
|
for operation in (mul, imul):
|
|
value = InheritedRepeat([1])
|
|
assert operation(value, ReflectedCount(2)) == "reflected"
|
|
assert value == [1]
|
|
|
|
|
|
class RepeatMixin:
|
|
def __rmul__(self, value):
|
|
return "mixin"
|
|
|
|
|
|
class MixedRepeat(list, RepeatMixin):
|
|
pass
|
|
|
|
|
|
# A secondary base can supply the numeric slot even if list hides its method.
|
|
assert mul(MixedRepeat([1]), ReflectedCount(2)) == [1, 1]
|
|
assert imul(ReflectedCount(2), MixedRepeat([1])) == [1, 1]
|
|
|
|
# Removing an override preserves an already installed numeric dispatcher.
|
|
InheritedRepeat.__imul__ = lambda self, other: NotImplemented
|
|
del InheritedRepeat.__imul__
|
|
value = InheritedRepeat([1])
|
|
assert imul(value, ReflectedCount(2)) is value
|
|
assert value == [1, 1]
|
|
|
|
|
|
class ReflectedAdd(list):
|
|
def __radd__(self, value):
|
|
return "reflected"
|
|
|
|
|
|
InheritedRepeat.__iadd__ = lambda self, other: NotImplemented
|
|
del InheritedRepeat.__iadd__
|
|
value = InheritedRepeat([1])
|
|
assert iadd(value, ReflectedAdd([2])) is value
|
|
assert value == [1, 2]
|