diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test index 5f2f0ae52101..1965f0cd1195 100644 --- a/mypyc/test-data/run-loops.test +++ b/mypyc/test-data/run-loops.test @@ -1,150 +1,314 @@ # Test cases for "range" objects, "for" and "while" loops (compile and run) -[case testFor] -from typing import Any, List, Tuple -def count(n: int) -> None: +[case testForRange] +from typing import Any + +from testutil import assertRaises + +def count(n: int) -> list[int]: + result = [] for i in range(n): - print(i) -def count_between(n: int, k: int) -> None: + result.append(i) + return result + +def count_between(n: int, k: int) -> list[int]: + result = [] for i in range(n, k): - print(i) - print('n=', n) -def count_down(n: int, k: int) -> None: + result.append(i) + result.append(n) + return result + +def count_down(n: int, k: int) -> list[int]: + result = [] for i in range(n, k, -1): - print(i) -def count_double(n: int, k: int) -> None: + result.append(i) + return result + +def count_double(n: int, k: int) -> list[int]: + result = [] for i in range(n, k, 2): - print(i) -def list_iter(l: List[int]) -> None: + result.append(i) + return result + +def count_down_short() -> list[int]: + result = [] + for i in range(10, 0, -1): + result.append(i) + return result + +def test_range_loop() -> None: + assert count(5) == [0, 1, 2, 3, 4] + assert count_between(11, 15) == [11, 12, 13, 14, 11] + n = 10**20 + assert count_between(n, n + 3) == [n, n + 1, n + 2, n] + assert count_down(20, 10) == list(range(20, 10, -1)) + assert count_double(10, 15) == [10, 12, 14] + assert count_down_short() == list(range(10, 0, -1)) + +def sum_range(x: range) -> int: + sum = 0 + for i in x: + sum += i + return sum + +def test_range_object() -> None: + r1 = range(4, 12, 2) + tmp_list = [x for x in r1] + assert tmp_list == [4, 6, 8, 10] + assert sum_range(r1) == 28 + r2: Any = range(10) + assert sum_range(r2) == 45 + r3: Any = 'x' + with assertRaises(TypeError, "range object expected; got str"): + sum_range(r3) + ff: Any = sum_range + with assertRaises(TypeError, "range object expected; got str"): + ff(r3) + with assertRaises(ValueError, "range() arg 3 must not be zero"): + range(4, 12, 0) + +def loop_end(end: Any) -> list[int]: + result = [] + for i in range(end): + result.append(i) + return result + +def loop(start: Any, end: Any) -> list[int]: + return [i for i in range(start, end)] + +def loop_down(start: Any) -> list[int]: + return [i for i in range(start, 0, -1)] + +def loop_down_to(start: Any, end: Any) -> list[int]: + return [i for i in range(start, end, -1)] + +def loop_bool(b: bool) -> list[int]: + return [i for i in range(b)] + +class NativeIndex: + def __init__(self, n: int) -> None: + self.n = n + + def __index__(self) -> int: + return self.n + +class NativeIndexSub(NativeIndex): + def __index__(self) -> int: + return self.n + 1 + +def loop_native(start: NativeIndex, end: NativeIndex) -> list[int]: + # The test fixtures only accept int arguments for range() + return [i for i in range(start, end)] # type: ignore[arg-type] + +def test_index_args() -> None: + from index_types import Index + assert loop_end(Index(3)) == [0, 1, 2] + assert loop(Index(1), Index(3)) == [1, 2] + assert loop_down(Index(2)) == [2, 1] + +def test_big_index_args() -> None: + from index_types import Index + # Ints >= 2**62 or < -2**62 don't fit in a short int, so these loops cross + # between short and heap-allocated values + n = 2**62 + assert loop(Index(n - 1), Index(n + 2)) == [n - 1, n, n + 1] + assert loop(Index(-n - 2), Index(-n + 1)) == [-n - 2, -n - 1, -n] + assert loop_down_to(Index(n + 1), Index(n - 2)) == [n + 1, n, n - 1] + assert loop_down_to(Index(-n + 1), Index(-n - 2)) == [-n + 1, -n, -n - 1] + assert loop_end(Index(-n - 1)) == [] + +def test_native_index_args() -> None: + assert loop_native(NativeIndex(1), NativeIndex(3)) == [1, 2] + assert loop_native(NativeIndexSub(1), NativeIndex(4)) == [2, 3] + n = 2**62 + assert loop_native(NativeIndex(n - 1), NativeIndex(n + 1)) == [n - 1, n] + +def test_int_args() -> None: + assert loop_end(3) == [0, 1, 2] + assert loop(True, 3) == [1, 2] + n = 2**100 + assert loop(n, n + 2) == [n, n + 1] + assert loop_bool(True) == [0] + assert loop_bool(False) == [] + +def test_invalid_args() -> None: + with assertRaises(TypeError, "'float' object cannot be interpreted as an integer"): + loop_end(2.5) + with assertRaises(TypeError, "'str' object cannot be interpreted as an integer"): + loop(0, "3") + with assertRaises(TypeError, "'float' object cannot be interpreted as an integer"): + loop_down(2.0) + +[file index_types.py] +class Index: + def __init__(self, n: int) -> None: + self.n = n + + def __index__(self) -> int: + return self.n + +[case testForSequences] +from collections.abc import Iterable +from typing import Any, NamedTuple, Optional +from typing_extensions import Self + +def list_iter(l: list[int]) -> list[int]: + result = [] for i in l: - print(i) -def tuple_iter(l: Tuple[int, ...]) -> None: + result.append(i) + return result + +def tuple_iter(l: tuple[int, ...]) -> list[int]: + result = [] for i in l: - print(i) -def empty_tuple_iter(l: Tuple[()]) -> None: + result.append(i) + return result + +def empty_tuple_iter(l: tuple[()]) -> list[Any]: + result = [] i: Any for i in l: - print(i) -def str_iter(l: str) -> None: + result.append(i) + return result + +def str_iter(l: str) -> list[str]: + result = [] for i in l: - print(i) -def list_rev_iter(l: List[int]) -> None: + result.append(i) + return result + +def list_rev_iter(l: list[int]) -> list[int]: + result = [] for i in reversed(l): - print(i) -def list_rev_iter_lol(l: List[int]) -> None: + result.append(i) + return result + +def list_rev_iter_lol(l: list[int]) -> list[int]: + result = [] for i in reversed(l): - print(i) + result.append(i) if i == 3: while l: l.pop() -def count_down_short() -> None: - for i in range(10, 0, -1): - print(i) -[file driver.py] -from native import ( - count, list_iter, list_rev_iter, list_rev_iter_lol, count_between, count_down, count_double, - count_down_short, tuple_iter, empty_tuple_iter, str_iter, -) -count(5) -list_iter(list(reversed(range(5)))) -list_rev_iter(list(reversed(range(5)))) -count_between(11, 15) -count_between(10**20, 10**20+3) -count_down(20, 10) -count_double(10, 15) -count_down_short() -print('==') -list_rev_iter_lol(list(reversed(range(5)))) -tuple_iter((1, 2, 3)) -empty_tuple_iter(()) -str_iter("abc") -[out] -0 -1 -2 -3 -4 -4 -3 -2 -1 -0 -0 -1 -2 -3 -4 -11 -12 -13 -14 -n= 11 -100000000000000000000 -100000000000000000001 -100000000000000000002 -n= 100000000000000000000 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -12 -14 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 -== -0 -1 -2 -3 -1 -2 -3 -a -b -c + return result -[case testLoopElse] -from typing import Iterator -def run_for_range(n: int) -> None: +def iterate_over_tuple(iterable: tuple[int, int, int]) -> list[int]: + result = [] + for element in iterable: + result.append(element) + return result + +def test_sequence_iteration() -> None: + assert list_iter([4, 3, 2, 1, 0]) == [4, 3, 2, 1, 0] + assert list_rev_iter([4, 3, 2, 1, 0]) == [0, 1, 2, 3, 4] + assert list_rev_iter_lol([4, 3, 2, 1, 0]) == [0, 1, 2, 3] + assert tuple_iter((1, 2, 3)) == [1, 2, 3] + assert empty_tuple_iter(()) == [] + assert str_iter("abc") == ["a", "b", "c"] + assert iterate_over_tuple((1, 2, 3)) == [1, 2, 3] + +def sum_over_values(d: dict[int, int]) -> int: + s = 0 + for key in d: + s = s + d[key] + return s + +def sum_over_even_values(d: dict[int, int]) -> int: + s = 0 + for key in d: + if d[key] % 2: + continue + s = s + d[key] + return s + +def sum_over_two_values(d: dict[int, int]) -> int: + s = 0 + i = 0 + for key in d: + if i == 2: + break + s = s + d[key] + i = i + 1 + return s + +def test_dict_iteration() -> None: + d = {1: 1, 2: 2, 3: 3, 4: 4, 5: 5} + assert sum_over_values(d) == 15 + assert sum_over_even_values(d) == 6 + assert sum_over_two_values(d) == 3 + +# Test inferring the type of a for loop body doesn't cause us grief +# Extracted from somethings that broke in mypy + +def iter_dict_after_isinstance(x: object) -> None: + if isinstance(x, dict): + for a in x: + pass + +def iter_mixed_tuple(x: Optional[str]) -> None: + vars = ( + ("a", 'lol'), + ("b", 'asdf'), + ("lol", x), + ("an int", 10), + ) + for name, value in vars: + pass + +def test_iter_type_trickiness() -> None: + iter_dict_after_isinstance({1: 2}) + iter_dict_after_isinstance(1) + iter_mixed_tuple(None) + +class Vector2(NamedTuple): + x: int + y: float + + @classmethod + def from_iter(cls, iterable: Iterable[Any]) -> Self: + return cls(*iter(iterable)) + + def __neg__(self) -> Self: + return self.from_iter(-c for c in self) + +def test_named_tuple_loop() -> None: + assert repr(-Vector2(2, -3.1)) == "Vector2(x=-2, y=3.1)" + assert [x for x in Vector2(4, -5.2)] == [4, -5.2] + +[case testLoopControlFlow] +from typing import Generator, Iterator + +def run_for_range(n: int) -> list[int]: + result = [] for i in range(n): if i == 3: break - print(i) + result.append(i) else: - print(n+1) + result.append(n + 1) + return result -def run_for_list(n: int) -> None: +def run_for_list(n: int) -> list[int]: + result = [] for i in list(range(n)): if i == 3: break - print(i) + result.append(i) else: - print(n+1) + result.append(n + 1) + return result -def run_for_iter(n: int) -> None: +def run_for_iter(n: int) -> list[int]: def identity(x: Iterator[int]) -> Iterator[int]: return x + result = [] for i in identity(range(n)): if i == 3: break - print(i) + result.append(i) else: - print(n+1) + result.append(n + 1) + return result def count(n: int) -> int: i = 1 @@ -176,49 +340,25 @@ def nested_for() -> int: return -1 return 0 -[file driver.py] -from native import run_for_range, run_for_list, run_for_iter, count, nested_while, nested_for -assert nested_while() == 0 -assert nested_for() == 0 -assert count(0) == -1 -assert count(1) == -2 -assert count(5) == 5 -assert count(6) == 5 -run_for_range(3) -run_for_range(5) -print('==') -run_for_list(3) -run_for_list(5) -print('==') -run_for_iter(3) -run_for_iter(5) -[out] -0 -1 -2 -4 -0 -1 -2 -== -0 -1 -2 -4 -0 -1 -2 -== -0 -1 -2 -4 -0 -1 -2 +def test_loop_else() -> None: + assert nested_while() == 0 + assert nested_for() == 0 + assert count(0) == -1 + assert count(1) == -2 + assert count(5) == 5 + assert count(6) == 5 + for f in run_for_range, run_for_list, run_for_iter: + assert f(3) == [0, 1, 2, 4] + assert f(5) == [0, 1, 2] + +def continue_for() -> int: + n = -1 + for n in range(5): + continue + return n -[case testNestedLoopSameIdx] -from typing import List, Generator +def test_continue_for() -> None: + assert continue_for() == 4 def nested_enumerate() -> None: l1 = [0,1,2] @@ -270,18 +410,17 @@ def nested_yield() -> Generator: yield i yield i +def test_nested_loop_same_index() -> None: + nested_enumerate() + nested_range() + nested_list() + assert list(nested_yield()) == [0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2] -[file driver.py] -from native import nested_enumerate, nested_range, nested_list, nested_yield -nested_enumerate() -nested_range() -nested_list() -gen = nested_yield() -expected = [0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2] -assert list(gen) == expected -[out] +[case testLoopVariableAfterLoop] +from typing import Iterator +from mypy_extensions import i64 +from testutil import assertRaises -[case testRangeLoopVariableAfterLoop] for i in range(3): pass assert i == 2, f"expected 2, got {i}" @@ -298,14 +437,8 @@ for m in range(0, 10, 2): pass assert m == 8, f"expected 8, got {m}" -[file driver.py] -from native import * -[out] - -[case testRangeLoopVariableEmptyRange] -from typing import Iterator -from mypy_extensions import i64 -from testutil import assertRaises +def test_range_loop_variable_at_module_level() -> None: + assert (i, j, k, m) == (2, 9, 1, 8) def last_range(start: int, end: int) -> int: i = -1 @@ -393,8 +526,52 @@ def test_empty_range_with_attribute_target() -> None: with assertRaises(AttributeError): A(0).x -[case testForLoopTargetEvaluatedEachIteration] -class C: +def get_enumerate_locals(iterable: list[int]) -> int: + for i, j in enumerate(iterable): + pass + try: + return i + except NameError: + return -100 + +def test_enumerate_empty_list() -> None: + assert get_enumerate_locals([]) == -100 + assert get_enumerate_locals([55]) == 0 + assert get_enumerate_locals([551, 552]) == 1 + +[case testForLoopTargets] +from typing import Any, Iterable + +# Test comprehensions and for loops with multiple index variables at module level +triples = [(1, 2, 'a'), (3, 4, 'b'), (5, 6, 'c')] +from_comprehension = [str(a*100+b)+' '+c for a, b, c in triples] +from_loop = [] +for a, b, c in triples: + from_loop.append(str(a*1000+b)+' '+c) + +def test_multiple_vars_at_module_level() -> None: + assert from_comprehension == ["102 a", "304 b", "506 c"] + assert from_loop == ["1002 a", "3004 b", "5006 c"] + +def zip_enumerate(a: Iterable[int], b: list[int]) -> list[Any]: + res = [] + for (x, y), z in zip(enumerate(a), b): + res.append((x, y, z)) + return res + +def enumerate_zip(a: Iterable[int], b: Iterable[str]) -> list[Any]: + res = [] + for x, (y, z) in enumerate(zip(a, b)): + res.append((x, y, z)) + return res + +def test_zip_and_enumerate() -> None: + assert zip_enumerate([6, 7], [8, 9]) == [(0, 6, 8), (1, 7, 9)] + assert enumerate_zip([6, 7], ['a', 'b']) == [(0, 6, 'a'), (1, 7, 'b')] + assert zip_enumerate([6, 7], [8]) == [(0, 6, 8)] + assert zip_enumerate([6], [8, 9]) == [(0, 6, 8)] + +class Recorder: def __init__(self) -> None: self.assigned: list[int] = [] @@ -407,7 +584,7 @@ class C: self.assigned.append(value) def test_attribute_target() -> None: - c = C() + c = Recorder() for c.x in range(0): pass assert c.assigned == [] @@ -415,18 +592,18 @@ def test_attribute_target() -> None: pass assert c.assigned == [0, 1, 2] -def index(log: list[int]) -> int: +def log_index(log: list[int]) -> int: log.append(0) return 0 def test_index_target_in_range_loop() -> None: a = [-1] log: list[int] = [] - for a[index(log)] in range(0): + for a[log_index(log)] in range(0): pass assert a == [-1] assert log == [] - for a[index(log)] in range(3): + for a[log_index(log)] in range(3): pass assert a == [2] assert len(log) == 3 @@ -435,48 +612,47 @@ def test_index_target_in_enumerate_loop() -> None: a = [-1] log: list[int] = [] empty: list[str] = [] - for a[index(log)], s in enumerate(empty): + for a[log_index(log)], s in enumerate(empty): pass assert a == [-1] assert log == [] - for a[index(log)], s in enumerate("abc"): + for a[log_index(log)], s in enumerate("abc"): pass assert a == [2] assert len(log) == 3 -[case testForZipAndEnumerateListClearedBeforeBody] -def index(items: list[int], calls: list[int]) -> int: +def clearing_index(items: list[int], calls: list[int]) -> int: calls.append(1) if len(calls) == 2: items.clear() return 0 -def test_enumerate_index_target() -> None: +def test_enumerate_index_target_clears_list() -> None: items = [10, 20] target = [-1] calls: list[int] = [] result: list[int] = [] - for target[index(items, calls)], value in enumerate(items): + for target[clearing_index(items, calls)], value in enumerate(items): result.append(value) assert items == [] assert result == [10, 20] -def test_zip_range_target() -> None: +def test_zip_range_target_clears_list() -> None: items = [10, 20] target = [-1] calls: list[int] = [] result: list[int] = [] - for target[index(items, calls)], value in zip(range(2), items): + for target[clearing_index(items, calls)], value in zip(range(2), items): result.append(value) assert items == [] assert result == [10, 20] -def test_zip_reversed() -> None: +def test_zip_reversed_target_clears_list() -> None: items = [10, 20] target = [-1] calls: list[int] = [] result: list[int] = [] - for target[index(items, calls)], value in zip([1, 2], reversed(items)): + for target[clearing_index(items, calls)], value in zip([1, 2], reversed(items)): result.append(value) assert items == [] assert result == [20, 10] @@ -495,7 +671,7 @@ def test_zip_iterator_clears_list() -> None: assert items == [] assert result == [(10, 1), (20, 2)] -class C: +class ClearingSetter: def __init__(self, items: list[int]) -> None: self.items = items @@ -508,17 +684,17 @@ class C: if value == 1: self.items.clear() -def test_enumerate_index_setter_clears_list() -> None: +def test_enumerate_attribute_setter_clears_list() -> None: items = [10, 20] - c = C(items) + c = ClearingSetter(items) result: list[int] = [] for c.x, value in enumerate(items): result.append(value) assert items == [] assert result == [10, 20] -[case testForIterable] -from typing import Iterable, Dict, Any, Tuple, TypeVar +[case testForIterableErrorTraceback] +from typing import Iterable, Dict, Any, TypeVar T = TypeVar("T") @@ -534,36 +710,8 @@ def iterate_and_delete(d: Dict[int, int]) -> None: for key in d: d.pop(key) -def sum_over_values(d: Dict[int, int]) -> int: - s = 0 - for key in d: - s = s + d[key] - return s - -def sum_over_even_values(d: Dict[int, int]) -> int: - s = 0 - for key in d: - if d[key] % 2: - continue - s = s + d[key] - return s - -def sum_over_two_values(d: Dict[int, int]) -> int: - s = 0 - i = 0 - for key in d: - if i == 2: - break - s = s + d[key] - i = i + 1 - return s - -def iterate_over_tuple(iterable: Tuple[int, int, int]) -> None: - for element in iterable: - print(element) - [file driver.py] -from native import iterate_over_any, iterate_over_iterable, iterate_and_delete, sum_over_values, sum_over_even_values, sum_over_two_values, iterate_over_tuple +from native import iterate_over_any, iterate_over_iterable, iterate_and_delete import traceback def broken_generator(n): num = 0 @@ -573,9 +721,6 @@ def broken_generator(n): raise Exception('Exception Manually Raised') d = {1:1, 2:2, 3:3, 4:4, 5:5} -print(sum_over_values(d)) -print(sum_over_even_values(d)) -print(sum_over_two_values(d)) try: iterate_over_any(5) @@ -589,17 +734,15 @@ try: iterate_and_delete(d) except RuntimeError: traceback.print_exc() - -iterate_over_tuple((1, 2, 3)) [out] Traceback (most recent call last): - File "driver.py", line 16, in + File "driver.py", line 13, in iterate_over_any(5) File "native.py", line 6, in iterate_over_any for element in a: TypeError: 'int' object is not iterable Traceback (most recent call last): - File "driver.py", line 20, in + File "driver.py", line 17, in iterate_over_iterable(broken_generator(5)) File "native.py", line 10, in iterate_over_iterable for element in iterable: @@ -607,32 +750,26 @@ Traceback (most recent call last): raise Exception('Exception Manually Raised') Exception: Exception Manually Raised Traceback (most recent call last): - File "driver.py", line 24, in + File "driver.py", line 21, in iterate_and_delete(d) File "native.py", line 14, in iterate_and_delete for key in d: RuntimeError: dictionary changed size during iteration -15 -6 -3 0 1 2 3 4 -1 -2 -3 [out version>=3.13] Traceback (most recent call last): - File "driver.py", line 16, in + File "driver.py", line 13, in iterate_over_any(5) ~~~~~~~~~~~~~~~~^^^ File "native.py", line 6, in iterate_over_any for element in a: TypeError: 'int' object is not iterable Traceback (most recent call last): - File "driver.py", line 20, in + File "driver.py", line 17, in iterate_over_iterable(broken_generator(5)) ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^ File "native.py", line 10, in iterate_over_iterable @@ -641,259 +778,14 @@ Traceback (most recent call last): raise Exception('Exception Manually Raised') Exception: Exception Manually Raised Traceback (most recent call last): - File "driver.py", line 24, in + File "driver.py", line 21, in iterate_and_delete(d) ~~~~~~~~~~~~~~~~~~^^^ File "native.py", line 14, in iterate_and_delete for key in d: RuntimeError: dictionary changed size during iteration -15 -6 -3 0 1 2 3 4 -1 -2 -3 - -[case testContinueFor] -def f() -> None: - for n in range(5): - continue -[file driver.py] -from native import f -f() - -[case testMultipleVarsWithLoops] -# Test comprehensions and for loops with multiple index variables -l = [(1, 2, 'a'), (3, 4, 'b'), (5, 6, 'c')] -l2 = [str(a*100+b)+' '+c for a, b, c in l] -l3 = [] -for a, b, c in l: - l3.append(str(a*1000+b)+' '+c) -[file driver.py] -from native import l, l2, l3 -for a in l2 + l3: - print(a) -[out] -102 a -304 b -506 c -1002 a -3004 b -5006 c - -[case testForZipAndEnumerate] -from typing import Iterable, List, Any -def f(a: Iterable[int], b: List[int]) -> List[Any]: - res = [] - for (x, y), z in zip(enumerate(a), b): - res.append((x, y, z)) - return res -def g(a: Iterable[int], b: Iterable[str]) -> List[Any]: - res = [] - for x, (y, z) in enumerate(zip(a, b)): - res.append((x, y, z)) - return res - -[file driver.py] -from native import f, g - -assert f([6, 7], [8, 9]) == [(0, 6, 8), (1, 7, 9)] -assert g([6, 7], ['a', 'b']) == [(0, 6, 'a'), (1, 7, 'b')] -assert f([6, 7], [8]) == [(0, 6, 8)] -assert f([6], [8, 9]) == [(0, 6, 8)] - -[case testEnumerateEmptyList] -from typing import List - -def get_enumerate_locals(iterable: List[int]) -> int: - for i, j in enumerate(iterable): - pass - try: - return i - except NameError: - return -100 - -[file driver.py] -from native import get_enumerate_locals - -print(get_enumerate_locals([])) -print(get_enumerate_locals([55])) -print(get_enumerate_locals([551, 552])) - -[out] --100 -0 -1 - -[case testIterTypeTrickiness] -# Test inferring the type of a for loop body doesn't cause us grief -# Extracted from somethings that broke in mypy - -from typing import Optional - -# really I only care that this one build -def foo(x: object) -> None: - if isinstance(x, dict): - for a in x: - pass - -def bar(x: Optional[str]) -> None: - vars = ( - ("a", 'lol'), - ("b", 'asdf'), - ("lol", x), - ("an int", 10), - ) - for name, value in vars: - pass - -[file driver.py] -from native import bar -bar(None) - -[case testRangeObject] -from typing import Any - -def f(x: range) -> int: - sum = 0 - for i in x: - sum += i - return sum - -def test_range_object() -> None: - r1 = range(4, 12, 2) - tmp_list = [x for x in r1] - assert tmp_list == [4, 6, 8, 10] - assert f(r1) == 28 - r2: Any = range(10) - assert f(r2) == 45 - r3: Any = 'x' - try: - f(r3) - except TypeError as e: - assert "range object expected; got str" in str(e) - try: - ff: Any = f - ff(r3) - except TypeError as e: - assert "range object expected; got str" in str(e) - try: - r4 = range(4, 12, 0) - except ValueError as e: - assert "range() arg 3 must not be zero" in str(e) - -[case testRangeLoopIndexArgs] -from typing import Any - -from testutil import assertRaises - -def loop_end(end: Any) -> list[int]: - result = [] - for i in range(end): - result.append(i) - return result - -def loop(start: Any, end: Any) -> list[int]: - return [i for i in range(start, end)] - -def loop_down(start: Any) -> list[int]: - return [i for i in range(start, 0, -1)] - -def loop_down_to(start: Any, end: Any) -> list[int]: - return [i for i in range(start, end, -1)] - -def loop_bool(b: bool) -> list[int]: - return [i for i in range(b)] - -class NativeIndex: - def __init__(self, n: int) -> None: - self.n = n - - def __index__(self) -> int: - return self.n - -class NativeIndexSub(NativeIndex): - def __index__(self) -> int: - return self.n + 1 - -def loop_native(start: NativeIndex, end: NativeIndex) -> list[int]: - # The test fixtures only accept int arguments for range() - return [i for i in range(start, end)] # type: ignore[arg-type] - -def test_index_args() -> None: - from index_types import Index - assert loop_end(Index(3)) == [0, 1, 2] - assert loop(Index(1), Index(3)) == [1, 2] - assert loop_down(Index(2)) == [2, 1] - -def test_big_index_args() -> None: - from index_types import Index - # Ints >= 2**62 or < -2**62 don't fit in a short int, so these loops cross - # between short and heap-allocated values - n = 2**62 - assert loop(Index(n - 1), Index(n + 2)) == [n - 1, n, n + 1] - assert loop(Index(-n - 2), Index(-n + 1)) == [-n - 2, -n - 1, -n] - assert loop_down_to(Index(n + 1), Index(n - 2)) == [n + 1, n, n - 1] - assert loop_down_to(Index(-n + 1), Index(-n - 2)) == [-n + 1, -n, -n - 1] - assert loop_end(Index(-n - 1)) == [] - -def test_native_index_args() -> None: - assert loop_native(NativeIndex(1), NativeIndex(3)) == [1, 2] - assert loop_native(NativeIndexSub(1), NativeIndex(4)) == [2, 3] - n = 2**62 - assert loop_native(NativeIndex(n - 1), NativeIndex(n + 1)) == [n - 1, n] - -def test_int_args() -> None: - assert loop_end(3) == [0, 1, 2] - assert loop(True, 3) == [1, 2] - n = 2**100 - assert loop(n, n + 2) == [n, n + 1] - assert loop_bool(True) == [0] - assert loop_bool(False) == [] - -def test_invalid_args() -> None: - with assertRaises(TypeError, "'float' object cannot be interpreted as an integer"): - loop_end(2.5) - with assertRaises(TypeError, "'str' object cannot be interpreted as an integer"): - loop(0, "3") - with assertRaises(TypeError, "'float' object cannot be interpreted as an integer"): - loop_down(2.0) - -[file index_types.py] -class Index: - def __init__(self, n: int) -> None: - self.n = n - - def __index__(self) -> int: - return self.n - -[case testNamedTupleLoop] -from collections.abc import Iterable -from typing import NamedTuple, Any -from typing_extensions import Self - - -class Vector2(NamedTuple): - x: int - y: float - - @classmethod - def from_iter(cls, iterable: Iterable[Any]) -> Self: - return cls(*iter(iterable)) - - def __neg__(self) -> Self: - return self.from_iter(-c for c in self) - -[file driver.py] -import native -print(-native.Vector2(2, -3.1)) -print([x for x in native.Vector2(4, -5.2)]) - -[out] -Vector2(x=-2, y=3.1) -\[4, -5.2]