| # Test cases for functions and calls (compile and run) | |
| [case testCallTrivialFunction] | |
| def f(x: int) -> int: | |
| return x | |
| [file driver.py] | |
| from native import f | |
| print(f(3)) | |
| print(f(-157)) | |
| print(f(10**20)) | |
| print(f(-10**20)) | |
| [out] | |
| 3 | |
| -157 | |
| 100000000000000000000 | |
| -100000000000000000000 | |
| [case testRecursiveFibonacci] | |
| def fib(n: int) -> int: | |
| if n <= 1: | |
| return 1 | |
| else: | |
| return fib(n - 1) + fib(n - 2) | |
| [file driver.py] | |
| from native import fib | |
| print(fib(0)) | |
| print(fib(1)) | |
| print(fib(2)) | |
| print(fib(6)) | |
| [out] | |
| 1 | |
| 1 | |
| 2 | |
| 13 | |
| [case testNestedFunctions] | |
| from typing import Callable, List | |
| def a() -> Callable[[], object]: | |
| def inner() -> object: | |
| return None | |
| return inner | |
| def b() -> Callable[[], Callable[[], str]]: | |
| def first() -> Callable[[], str]: | |
| def second() -> str: | |
| return 'b.first.second: nested function' | |
| return second | |
| return first | |
| def c(num: float) -> Callable[[str], str]: | |
| def inner(s: str) -> str: | |
| return s + '!' | |
| return inner | |
| def d(num: float) -> str: | |
| def inner(s: str) -> str: | |
| return s + '?' | |
| a = inner('one') | |
| b = inner('two') | |
| return a | |
| def e() -> int: | |
| return 0 | |
| def f() -> int: | |
| def inner() -> int: | |
| return e() | |
| return inner() | |
| def g() -> Callable[[], Callable[[], int]]: | |
| def inner() -> Callable[[], int]: | |
| return e | |
| return inner | |
| def h(num: int) -> int: | |
| def inner() -> int: | |
| return num | |
| return inner() | |
| def i() -> int: | |
| num = 3 | |
| def inner() -> int: | |
| return num | |
| return inner() | |
| def j(num: int) -> int: | |
| x = 1 | |
| y = 2 | |
| def inner() -> int: | |
| nonlocal x | |
| x = 3 | |
| return num + x + y | |
| return inner() | |
| def k() -> int: | |
| num = 3 | |
| def inner() -> int: | |
| nonlocal num | |
| num = 5 | |
| return num | |
| return inner() + num | |
| def l() -> int: | |
| num = 3 | |
| def inner() -> int: | |
| num = 5 | |
| return num | |
| return inner() + num | |
| def m() -> Callable[[], int]: | |
| num = 1 | |
| def inner() -> int: | |
| num += 1 | |
| return num | |
| num += 1 | |
| return inner | |
| def n() -> int: | |
| x = 1 | |
| def add_one() -> None: | |
| x += 1 | |
| def add_two() -> None: | |
| x += 2 | |
| add_one() | |
| add_two() | |
| return x | |
| def triple(a: int) -> Callable[[], Callable[[int], int]]: | |
| x = 1 | |
| def outer() -> Callable[[int], int]: | |
| nonlocal x | |
| x += 1 | |
| x += a | |
| a += 1 | |
| def inner(b: int) -> int: | |
| x += b | |
| return x | |
| return inner | |
| return outer | |
| def if_else(flag: int) -> str: | |
| def dummy_function() -> str: | |
| return 'if_else.dummy_function' | |
| if flag < 0: | |
| def inner() -> str: | |
| return 'if_else.inner: first definition' | |
| elif flag > 0: | |
| def inner() -> str: | |
| return 'if_else.inner: second definition' | |
| else: | |
| def inner() -> str: | |
| return 'if_else.inner: third definition' | |
| return inner() | |
| def wrap(f: Callable[[], str]): | |
| def inner(): | |
| return 'wrapped ' + f() | |
| return inner | |
| def if_else_all_decorated(flag: int) -> str: | |
| def dummy_function() -> str: | |
| return 'if_else.dummy_function' | |
| if flag < 0: | |
| @wrap | |
| def inner() -> str: | |
| return 'if_else.inner: first definition' | |
| elif flag > 0: | |
| @wrap | |
| def inner() -> str: | |
| return 'if_else.inner: second definition' | |
| else: | |
| @wrap | |
| def inner() -> str: | |
| return 'if_else.inner: third definition' | |
| return inner() | |
| def if_else_first_decorated(flag: int) -> str: | |
| def dummy_function() -> str: | |
| return 'if_else.dummy_function' | |
| if flag < 0: | |
| @wrap | |
| def inner() -> str: | |
| return 'if_else.inner: first definition' | |
| elif flag > 0: | |
| def inner() -> str: | |
| return 'if_else.inner: second definition' | |
| else: | |
| def inner() -> str: | |
| return 'if_else.inner: third definition' | |
| return inner() | |
| def if_else_all_but_first_decorated(flag: int) -> str: | |
| def dummy_function() -> str: | |
| return 'if_else.dummy_function' | |
| if flag < 0: | |
| def inner() -> str: | |
| return 'if_else.inner: first definition' | |
| elif flag > 0: | |
| @wrap | |
| def inner() -> str: | |
| return 'if_else.inner: second definition' | |
| else: | |
| @wrap | |
| def inner() -> str: | |
| return 'if_else.inner: third definition' | |
| return inner() | |
| def for_loop() -> int: | |
| def dummy_function() -> str: | |
| return 'for_loop.dummy_function' | |
| for i in range(5): | |
| def inner(i: int) -> int: | |
| return i | |
| if i == 3: | |
| return inner(i) | |
| return 0 | |
| def while_loop() -> int: | |
| def dummy_function() -> str: | |
| return 'while_loop.dummy_function' | |
| i = 0 | |
| while i < 5: | |
| def inner(i: int) -> int: | |
| return i | |
| if i == 3: | |
| return inner(i) | |
| i += 1 | |
| return 0 | |
| def free_vars(foo: int, bar: int) -> int: | |
| x = 1 | |
| y = 2 | |
| def g(): # type: ignore # missing type annotation for testing | |
| nonlocal y | |
| y = 3 | |
| nonlocal bar | |
| bar += y | |
| z = 3 | |
| g() | |
| return bar | |
| def lambdas(x: int, y: int) -> int: | |
| s = lambda a, b: a + b + x + y | |
| return s(1, 2) | |
| def outer() -> str: | |
| return 'outer: normal function' | |
| def inner() -> str: | |
| return 'inner: normal function' | |
| class A: | |
| def __init__(self, x: int) -> None: | |
| self.x = x | |
| def outer(self, num: int) -> int: | |
| y = 5 | |
| def inner() -> int: | |
| return self.x + y + num | |
| return inner() | |
| def o() -> int: | |
| a = [0, 0] | |
| b = 0 | |
| def b_incr() -> List[int]: | |
| b += 10 | |
| return a | |
| c = 0 | |
| def c_incr() -> int: | |
| c += 1 | |
| return c | |
| # x = 1, y = 1 | |
| x = y = c_incr() | |
| # a = [2, 2], b = 20 | |
| b_incr()[0] = b_incr()[1] = c_incr() | |
| # Should return 26. | |
| return x + y + a[0] + a[1] + b | |
| global_upvar = 20 | |
| toplevel_lambda = lambda x: 10 + global_upvar + x | |
| [file driver.py] | |
| from native import ( | |
| a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, triple, if_else, if_else_all_decorated, | |
| if_else_first_decorated, if_else_all_but_first_decorated, | |
| for_loop, while_loop, free_vars, lambdas, outer, inner, A, toplevel_lambda | |
| ) | |
| assert a()() == None | |
| assert b()()() == 'b.first.second: nested function' | |
| assert c(5.0)('c') == 'c!' | |
| assert d(4.0) == 'one?' | |
| assert e() == 0 | |
| assert f() == 0 | |
| assert g()()() == 0 | |
| assert h(3) == 3 | |
| assert i() == 3 | |
| assert j(3) == 8 | |
| assert k() == 10 | |
| assert l() == 8 | |
| assert m()() == 3 | |
| assert n() == 4 | |
| assert o() == 26 | |
| triple_outer = triple(2) | |
| triple_inner = triple_outer() | |
| assert triple_inner(4) == 8 | |
| assert triple_inner(4) == 12 | |
| assert triple_outer()(4) == 20 | |
| assert if_else(-1) == 'if_else.inner: first definition' | |
| assert if_else(1) == 'if_else.inner: second definition' | |
| assert if_else(0) == 'if_else.inner: third definition' | |
| assert if_else_all_decorated(-1) == 'wrapped if_else.inner: first definition' | |
| assert if_else_all_decorated(1) == 'wrapped if_else.inner: second definition' | |
| assert if_else_all_decorated(0) == 'wrapped if_else.inner: third definition' | |
| assert if_else_first_decorated(-1) == 'wrapped if_else.inner: first definition' | |
| assert if_else_first_decorated(1) == 'if_else.inner: second definition' | |
| assert if_else_first_decorated(0) == 'if_else.inner: third definition' | |
| assert if_else_all_but_first_decorated(-1) == 'if_else.inner: first definition' | |
| assert if_else_all_but_first_decorated(1) == 'wrapped if_else.inner: second definition' | |
| assert if_else_all_but_first_decorated(0) == 'wrapped if_else.inner: third definition' | |
| assert for_loop() == 3 | |
| assert while_loop() == 3 | |
| assert free_vars(1, 2) == 5 | |
| assert lambdas(3, 4) == 10 | |
| assert outer() == 'outer: normal function' | |
| assert inner() == 'inner: normal function' | |
| assert A(3).outer(4) == 12 | |
| assert toplevel_lambda(5) == 35 | |
| [case testNestedFunctions2] | |
| from typing import Callable | |
| def outer() -> Callable[[], object]: | |
| def inner() -> object: | |
| return None | |
| return inner | |
| def first() -> Callable[[], Callable[[], str]]: | |
| def second() -> Callable[[], str]: | |
| def third() -> str: | |
| return 'third: nested function' | |
| return third | |
| return second | |
| def f1() -> int: | |
| x = 1 | |
| def f2() -> int: | |
| y = 2 | |
| def f3() -> int: | |
| z = 3 | |
| return y | |
| return f3() | |
| return f2() | |
| def outer_func() -> int: | |
| def inner_func() -> int: | |
| return x | |
| x = 1 | |
| return inner_func() | |
| def mutual_recursion(start : int) -> int: | |
| def f1(k : int) -> int: | |
| if k <= 0: | |
| return 0 | |
| k -= 1 | |
| return f2(k) | |
| def f2(k : int) -> int: | |
| if k <= 0: | |
| return 0 | |
| k -= 1 | |
| return f1(k) | |
| return f1(start) | |
| def topLayer() -> int: | |
| def middleLayer() -> int: | |
| def bottomLayer() -> int: | |
| return x | |
| return bottomLayer() | |
| x = 1 | |
| return middleLayer() | |
| def nest1() -> str: | |
| def nest2() -> str: | |
| def nest3() -> str: | |
| def mut1(val: int) -> str: | |
| if val <= 0: | |
| return "bottomed" | |
| val -= 1 | |
| return mut2(val) | |
| def mut2(val: int) -> str: | |
| if val <= 0: | |
| return "bottomed" | |
| val -= 1 | |
| return mut1(val) | |
| return mut1(start) | |
| return nest3() | |
| start = 3 | |
| return nest2() | |
| def uno(num: float) -> Callable[[str], str]: | |
| def dos(s: str) -> str: | |
| return s + '!' | |
| return dos | |
| def eins(num: float) -> str: | |
| def zwei(s: str) -> str: | |
| return s + '?' | |
| a = zwei('eins') | |
| b = zwei('zwei') | |
| return a | |
| def call_other_inner_func(a: int) -> int: | |
| def foo() -> int: | |
| return a + 1 | |
| def bar() -> int: | |
| return foo() | |
| def baz(n: int) -> int: | |
| if n == 0: | |
| return 0 | |
| return n + baz(n - 1) | |
| return bar() + baz(a) | |
| def inner() -> str: | |
| return 'inner: normal function' | |
| def second() -> str: | |
| return 'second: normal function' | |
| def third() -> str: | |
| return 'third: normal function' | |
| [file driver.py] | |
| from native import (outer, inner, first, uno, eins, call_other_inner_func, | |
| second, third, f1, outer_func, mutual_recursion, topLayer, nest1) | |
| assert outer()() == None | |
| assert inner() == 'inner: normal function' | |
| assert first()()() == 'third: nested function' | |
| assert uno(5.0)('uno') == 'uno!' | |
| assert eins(4.0) == 'eins?' | |
| assert call_other_inner_func(5) == 21 | |
| assert second() == 'second: normal function' | |
| assert third() == 'third: normal function' | |
| assert f1() == 2 | |
| assert outer_func() == 1 | |
| assert mutual_recursion(5) == 0 | |
| assert topLayer() == 1 | |
| assert nest1() == "bottomed" | |
| [case testFunctionCallWithDefaultArgs] | |
| from typing import Tuple, List, Optional, Callable, Any | |
| def f(x: int, y: int = 3, s: str = "test", z: object = 5) -> Tuple[int, str]: | |
| def inner() -> int: | |
| return x + y | |
| return inner(), s | |
| def g() -> None: | |
| assert f(2) == (5, "test") | |
| assert f(s = "123", x = -2) == (1, "123") | |
| def h(a: Optional[object] = None, b: Optional[str] = None) -> Tuple[object, Optional[str]]: | |
| return (a, b) | |
| def same(x: object = object()) -> object: | |
| return x | |
| a_lambda: Callable[..., Any] = lambda n=20: n | |
| def nested_funcs(n: int) -> List[Callable[..., Any]]: | |
| ls: List[Callable[..., Any]] = [] | |
| for i in range(n): | |
| def f(i: int = i) -> int: | |
| return i | |
| ls.append(f) | |
| return ls | |
| def bool_default(x: bool = False, y: bool = True) -> str: | |
| return str(x) + '-' + str(y) | |
| [file driver.py] | |
| from native import f, g, h, same, nested_funcs, a_lambda, bool_default | |
| g() | |
| assert f(2) == (5, "test") | |
| assert f(s = "123", x = -2) == (1, "123") | |
| assert h() == (None, None) | |
| assert h(10) == (10, None) | |
| assert h(b='a') == (None, 'a') | |
| assert h(10, 'a') == (10, 'a') | |
| assert same() == same() | |
| assert [f() for f in nested_funcs(10)] == list(range(10)) | |
| assert a_lambda(10) == 10 | |
| assert a_lambda() == 20 | |
| assert bool_default() == 'False-True' | |
| assert bool_default(True) == 'True-True' | |
| assert bool_default(True, False) == 'True-False' | |
| [case testMethodCallWithDefaultArgs] | |
| from typing import Tuple, List | |
| class A: | |
| def f(self, x: int, y: int = 3, s: str = "test") -> Tuple[int, str]: | |
| def inner() -> int: | |
| return x + y | |
| return inner(), s | |
| def g() -> None: | |
| a = A() | |
| assert a.f(2) == (5, "test") | |
| assert a.f(s = "123", x = -2) == (1, "123") | |
| [file driver.py] | |
| from native import A, g | |
| g() | |
| a = A() | |
| assert a.f(2) == (5, "test") | |
| assert a.f(s = "123", x = -2) == (1, "123") | |
| [case testMethodCallOrdering] | |
| class A: | |
| def __init__(self, s: str) -> None: | |
| print(s) | |
| def f(self, x: 'A', y: 'A') -> None: | |
| pass | |
| def g() -> None: | |
| A('A!').f(A('hello'), A('world')) | |
| [file driver.py] | |
| from native import g | |
| g() | |
| [out] | |
| A! | |
| hello | |
| world | |
| [case testPyMethodCall] | |
| from typing import List | |
| def f(x: List[int]) -> int: | |
| return x.pop() | |
| def g(x: List[int], y: List[int]) -> None: | |
| x.extend(y) | |
| [file driver.py] | |
| from native import f, g | |
| l = [1, 2] | |
| assert f(l) == 2 | |
| g(l, [10]) | |
| assert l == [1, 10] | |
| assert f(l) == 10 | |
| assert f(l) == 1 | |
| g(l, [11, 12]) | |
| assert l == [11, 12] | |
| [case testMethodCallWithKeywordArgs] | |
| from typing import Tuple | |
| import testmodule | |
| class A: | |
| def echo(self, a: int, b: int, c: int) -> Tuple[int, int, int]: | |
| return a, b, c | |
| def test_native_method_call_with_kwargs() -> None: | |
| a = A() | |
| assert a.echo(1, c=3, b=2) == (1, 2, 3) | |
| assert a.echo(c = 3, a = 1, b = 2) == (1, 2, 3) | |
| def test_module_method_call_with_kwargs() -> None: | |
| a = testmodule.A() | |
| assert a.echo(1, c=3, b=2) == (1, 2, 3) | |
| assert a.echo(c = 3, a = 1, b = 2) == (1, 2, 3) | |
| [file testmodule.py] | |
| from typing import Tuple | |
| class A: | |
| def echo(self, a: int, b: int, c: int) -> Tuple[int, int, int]: | |
| return a, b, c | |
| [file driver.py] | |
| import native | |
| native.test_native_method_call_with_kwargs() | |
| native.test_module_method_call_with_kwargs() | |
| [case testAnyCall] | |
| from typing import Any | |
| def call(f: Any) -> Any: | |
| return f(1, 'x') | |
| [file driver.py] | |
| from native import call | |
| def f(x, y): | |
| return (x, y) | |
| def g(x): pass | |
| assert call(f) == (1, 'x') | |
| for bad in g, 1: | |
| try: | |
| call(bad) | |
| except TypeError: | |
| pass | |
| else: | |
| assert False, bad | |
| [case testCallableTypes] | |
| from typing import Callable | |
| def absolute_value(x: int) -> int: | |
| return x if x > 0 else -x | |
| def call_native_function(x: int) -> int: | |
| return absolute_value(x) | |
| def call_python_function(x: int) -> int: | |
| return int(x) | |
| def return_float() -> float: | |
| return 5.0 | |
| def return_callable_type() -> Callable[[], float]: | |
| return return_float | |
| def call_callable_type() -> float: | |
| f = return_callable_type() | |
| return f() | |
| def return_passed_in_callable_type(f: Callable[[], float]) -> Callable[[], float]: | |
| return f | |
| def call_passed_in_callable_type(f: Callable[[], float]) -> float: | |
| return f() | |
| [file driver.py] | |
| from native import call_native_function, call_python_function, return_float, return_callable_type, call_callable_type, return_passed_in_callable_type, call_passed_in_callable_type | |
| a = call_native_function(1) | |
| b = call_python_function(1) | |
| c = return_callable_type() | |
| d = call_callable_type() | |
| e = return_passed_in_callable_type(return_float) | |
| f = call_passed_in_callable_type(return_float) | |
| assert a == 1 | |
| assert b == 1 | |
| assert c() == 5.0 | |
| assert d == 5.0 | |
| assert e() == 5.0 | |
| assert f == 5.0 | |
| [case testKeywordArgs] | |
| from typing import Tuple | |
| import testmodule | |
| def g(a: int, b: int, c: int) -> Tuple[int, int, int]: | |
| return a, b, c | |
| def test_call_native_function_with_keyword_args() -> None: | |
| assert g(1, c = 3, b = 2) == (1, 2, 3) | |
| assert g(c = 3, a = 1, b = 2) == (1, 2, 3) | |
| def test_call_module_function_with_keyword_args() -> None: | |
| assert testmodule.g(1, c = 3, b = 2) == (1, 2, 3) | |
| assert testmodule.g(c = 3, a = 1, b = 2) == (1, 2, 3) | |
| def test_call_python_function_with_keyword_args() -> None: | |
| assert int("11", base=2) == 3 | |
| def test_call_lambda_function_with_keyword_args() -> None: | |
| g = testmodule.get_lambda_function() | |
| assert g(1, c = 3, b = 2) == (1, 2, 3) | |
| assert g(c = 3, a = 1, b = 2) == (1, 2, 3) | |
| [file testmodule.py] | |
| from typing import Tuple | |
| def g(a: int, b: int, c: int) -> Tuple[int, int, int]: | |
| return a, b, c | |
| def get_lambda_function(): | |
| return (lambda a, b, c: (a, b, c)) | |
| [file driver.py] | |
| import native | |
| native.test_call_native_function_with_keyword_args() | |
| native.test_call_module_function_with_keyword_args() | |
| native.test_call_python_function_with_keyword_args() | |
| native.test_call_lambda_function_with_keyword_args() | |
| [case testStarArgs] | |
| from typing import Tuple | |
| def g(a: int, b: int, c: int) -> Tuple[int, int, int]: | |
| return a, b, c | |
| def test_star_args() -> None: | |
| assert g(*[1, 2, 3]) == (1, 2, 3) | |
| assert g(*(1, 2, 3)) == (1, 2, 3) | |
| assert g(*(1,), *[2, 3]) == (1, 2, 3) | |
| assert g(*(), *(1,), *(), *(2,), *(3,), *()) == (1, 2, 3) | |
| assert g(*range(3)) == (0, 1, 2) | |
| [file driver.py] | |
| import native | |
| native.test_star_args() | |
| [case testStar2Args] | |
| from typing import Tuple | |
| def g(a: int, b: int, c: int) -> Tuple[int, int, int]: | |
| return a, b, c | |
| def test_star2_args() -> None: | |
| assert g(**{'a': 1, 'b': 2, 'c': 3}) == (1, 2, 3) | |
| assert g(**{'c': 3, 'a': 1, 'b': 2}) == (1, 2, 3) | |
| assert g(b=2, **{'a': 1, 'c': 3}) == (1, 2, 3) | |
| def test_star2_args_bad(v: dict) -> bool: | |
| return g(a=1, b=2, **v) == (1, 2, 3) | |
| [file driver.py] | |
| import native | |
| native.test_star2_args() | |
| # this should raise TypeError due to duplicate kwarg, but currently it doesn't | |
| assert native.test_star2_args_bad({'b': 2, 'c': 3}) | |
| [case testStarAndStar2Args] | |
| from typing import Tuple | |
| def g(a: int, b: int, c: int) -> Tuple[int, int, int]: | |
| return a, b, c | |
| class C: | |
| def g(self, a: int, b: int, c: int) -> Tuple[int, int, int]: | |
| return a, b, c | |
| def test_star_and_star2_args() -> None: | |
| assert g(1, *(2,), **{'c': 3}) == (1, 2, 3) | |
| assert g(*[1], **{'b': 2, 'c': 3}) == (1, 2, 3) | |
| c = C() | |
| assert c.g(1, *(2,), **{'c': 3}) == (1, 2, 3) | |
| assert c.g(*[1], **{'b': 2, 'c': 3}) == (1, 2, 3) | |
| [file driver.py] | |
| import native | |
| native.test_star_and_star2_args() | |
| [case testAllTheArgCombinations] | |
| from typing import Tuple | |
| def g(a: int, b: int, c: int, d: int = -1) -> Tuple[int, int, int, int]: | |
| return a, b, c, d | |
| class C: | |
| def g(self, a: int, b: int, c: int, d: int = -1) -> Tuple[int, int, int, int]: | |
| return a, b, c, d | |
| def test_all_the_arg_combinations() -> None: | |
| assert g(1, *(2,), **{'c': 3}) == (1, 2, 3, -1) | |
| assert g(*[1], **{'b': 2, 'c': 3, 'd': 4}) == (1, 2, 3, 4) | |
| c = C() | |
| assert c.g(1, *(2,), **{'c': 3}) == (1, 2, 3, -1) | |
| assert c.g(*[1], **{'b': 2, 'c': 3, 'd': 4}) == (1, 2, 3, 4) | |
| [file driver.py] | |
| import native | |
| native.test_all_the_arg_combinations() | |
| [case testOverloads] | |
| from typing import overload, Union, Tuple | |
| @overload | |
| def foo(x: int) -> int: ... | |
| @overload | |
| def foo(x: str) -> str: ... | |
| def foo(x: Union[int, str]) -> Union[int, str]: | |
| return x | |
| class A: | |
| @overload | |
| def foo(self, x: int) -> int: ... | |
| @overload | |
| def foo(self, x: str) -> str: ... | |
| def foo(self, x: Union[int, str]) -> Union[int, str]: | |
| return x | |
| def call1() -> Tuple[int, str]: | |
| return (foo(10), foo('10')) | |
| def call2() -> Tuple[int, str]: | |
| x = A() | |
| return (x.foo(10), x.foo('10')) | |
| [file driver.py] | |
| from native import * | |
| assert call1() == (10, '10') | |
| assert call2() == (10, '10') | |
| [case testDecorators1] | |
| from typing import Generator, Callable, Iterator | |
| from contextlib import contextmanager | |
| def a(f: Callable[[], None]) -> Callable[[], None]: | |
| def g() -> None: | |
| print('Entering') | |
| f() | |
| print('Exited') | |
| return g | |
| def b(f: Callable[[], None]) -> Callable[[], None]: | |
| def g() -> None: | |
| print('***') | |
| f() | |
| print('***') | |
| return g | |
| @contextmanager | |
| def foo() -> Iterator[int]: | |
| try: | |
| print('started') | |
| yield 0 | |
| finally: | |
| print('finished') | |
| @contextmanager | |
| def catch() -> Iterator[None]: | |
| try: | |
| print('started') | |
| yield | |
| except IndexError: | |
| print('index') | |
| raise | |
| except Exception: | |
| print('lol') | |
| def thing() -> None: | |
| c() | |
| @a | |
| @b | |
| def c() -> None: | |
| @a | |
| @b | |
| def d() -> None: | |
| print('d') | |
| print('c') | |
| d() | |
| def hm() -> None: | |
| x = [1] | |
| with catch(): | |
| x[2] | |
| [file driver.py] | |
| from native import foo, c, thing, hm | |
| with foo() as f: | |
| print('hello') | |
| c() | |
| thing() | |
| print('==') | |
| try: | |
| hm() | |
| except IndexError: | |
| pass | |
| else: | |
| assert False | |
| [out] | |
| started | |
| hello | |
| finished | |
| Entering | |
| *** | |
| c | |
| Entering | |
| *** | |
| d | |
| *** | |
| Exited | |
| *** | |
| Exited | |
| Entering | |
| *** | |
| c | |
| Entering | |
| *** | |
| d | |
| *** | |
| Exited | |
| *** | |
| Exited | |
| == | |
| started | |
| index | |
| [case testDecoratorsMethods] | |
| from typing import Any, Callable, Iterator, TypeVar | |
| from contextlib import contextmanager | |
| T = TypeVar('T') | |
| def dec(f: T) -> T: | |
| return f | |
| def a(f: Callable[[Any], None]) -> Callable[[Any], None]: | |
| def g(a: Any) -> None: | |
| print('Entering') | |
| f(a) | |
| print('Exited') | |
| return g | |
| class A: | |
| @a | |
| def foo(self) -> None: | |
| print('foo') | |
| @contextmanager | |
| def generator(self) -> Iterator[int]: | |
| try: | |
| print('contextmanager: entering') | |
| yield 0 | |
| finally: | |
| print('contextmanager: exited') | |
| class Lol: | |
| @staticmethod | |
| def foo() -> None: | |
| Lol.bar() | |
| Lol.baz() | |
| @staticmethod | |
| @dec | |
| def bar() -> None: | |
| pass | |
| @classmethod | |
| @dec | |
| def baz(cls) -> None: | |
| pass | |
| def inside() -> None: | |
| with A().generator() as g: | |
| print('hello!') | |
| with A().generator() as g: | |
| print('hello!') | |
| def lol() -> None: | |
| with A().generator() as g: | |
| raise Exception | |
| [file driver.py] | |
| from native import A, lol | |
| A.foo(A()) | |
| A().foo() | |
| with A().generator() as g: | |
| print('hello!') | |
| try: | |
| lol() | |
| except: | |
| pass | |
| else: | |
| assert False | |
| [out] | |
| contextmanager: entering | |
| hello! | |
| contextmanager: exited | |
| Entering | |
| foo | |
| Exited | |
| Entering | |
| foo | |
| Exited | |
| contextmanager: entering | |
| hello! | |
| contextmanager: exited | |
| contextmanager: entering | |
| contextmanager: exited | |
| [case testUnannotatedFunction] | |
| def g(x: int) -> int: | |
| return x * 2 | |
| def f(x): | |
| return g(x) | |
| [file driver.py] | |
| from native import f | |
| assert f(3) == 6 | |
| [case testUnannotatedModuleLevelInitFunction] | |
| # Ensure that adding an implicit `-> None` annotation only applies to `__init__` | |
| # _methods_ specifically (not module-level `__init__` functions). | |
| def __init__(): | |
| return 42 | |
| [file driver.py] | |
| from native import __init__ | |
| assert __init__() == 42 | |
| [case testDifferentArgCountsFromInterpreted] | |
| # Test various signatures from interpreted code. | |
| def noargs() -> int: | |
| return 5 | |
| def onearg(x: int) -> int: | |
| return x + 1 | |
| def twoargs(x: int, y: str) -> int: | |
| return x + len(y) | |
| def one_or_two(x: int, y: str = 'a') -> int: | |
| return x + len(y) | |
| [file driver.py] | |
| from native import noargs, onearg, twoargs, one_or_two | |
| from testutil import assertRaises | |
| assert noargs() == 5 | |
| t = () | |
| assert noargs(*t) == 5 | |
| d = {} | |
| assert noargs(**d) == 5 | |
| assert noargs(*t, **d) == 5 | |
| assert onearg(12) == 13 | |
| assert onearg(x=8) == 9 | |
| t = (1,) | |
| assert onearg(*t) == 2 | |
| d = {'x': 5} | |
| assert onearg(**d) == 6 | |
| # Test a bogus call to twoargs before any correct calls are made | |
| with assertRaises(TypeError, "twoargs() missing required argument 'x' (pos 1)"): | |
| twoargs() | |
| assert twoargs(5, 'foo') == 8 | |
| assert twoargs(4, y='foo') == 7 | |
| assert twoargs(y='foo', x=7) == 10 | |
| t = (1, 'xy') | |
| assert twoargs(*t) == 3 | |
| d = {'y': 'xy'} | |
| assert twoargs(2, **d) == 4 | |
| assert one_or_two(5) == 6 | |
| assert one_or_two(x=3) == 4 | |
| assert one_or_two(6, 'xy') == 8 | |
| assert one_or_two(7, y='xy') == 9 | |
| assert one_or_two(y='xy', x=2) == 4 | |
| assert one_or_two(*t) == 3 | |
| d = {'x': 5} | |
| assert one_or_two(**d) == 6 | |
| assert one_or_two(y='xx', **d) == 7 | |
| d = {'y': 'abc'} | |
| assert one_or_two(1, **d) == 4 | |
| with assertRaises(TypeError, 'noargs() takes at most 0 arguments (1 given)'): | |
| noargs(1) | |
| with assertRaises(TypeError, 'noargs() takes at most 0 keyword arguments (1 given)'): | |
| noargs(x=1) | |
| with assertRaises(TypeError, "onearg() missing required argument 'x' (pos 1)"): | |
| onearg() | |
| with assertRaises(TypeError, 'onearg() takes at most 1 argument (2 given)'): | |
| onearg(1, 2) | |
| with assertRaises(TypeError, "onearg() missing required argument 'x' (pos 1)"): | |
| onearg(y=1) | |
| with assertRaises(TypeError, "onearg() takes at most 1 argument (2 given)"): | |
| onearg(1, y=1) | |
| with assertRaises(TypeError, "twoargs() missing required argument 'x' (pos 1)"): | |
| twoargs() | |
| with assertRaises(TypeError, "twoargs() missing required argument 'y' (pos 2)"): | |
| twoargs(1) | |
| with assertRaises(TypeError, 'twoargs() takes at most 2 arguments (3 given)'): | |
| twoargs(1, 'x', 2) | |
| with assertRaises(TypeError, 'twoargs() takes at most 2 arguments (3 given)'): | |
| twoargs(1, 'x', y=2) | |
| with assertRaises(TypeError, "one_or_two() missing required argument 'x' (pos 1)"): | |
| one_or_two() | |
| with assertRaises(TypeError, 'one_or_two() takes at most 2 arguments (3 given)'): | |
| one_or_two(1, 'x', 2) | |
| with assertRaises(TypeError, 'one_or_two() takes at most 2 arguments (3 given)'): | |
| one_or_two(1, 'x', y=2) | |
| [case testComplicatedArgs] | |
| from typing import Tuple, Dict | |
| def kwonly1(x: int = 0, *, y: int) -> Tuple[int, int]: | |
| return x, y | |
| def kwonly2(*, x: int = 0, y: int) -> Tuple[int, int]: | |
| return x, y | |
| def kwonly3(a: int, b: int = 0, *, y: int, x: int = 1) -> Tuple[int, int, int, int]: | |
| return a, b, x, y | |
| def kwonly4(*, x: int, y: int) -> Tuple[int, int]: | |
| return x, y | |
| def varargs1(*args: int) -> Tuple[int, ...]: | |
| return args | |
| def varargs2(*args: int, **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: | |
| return args, kwargs | |
| def varargs3(**kwargs: int) -> Dict[str, int]: | |
| return kwargs | |
| def varargs4(a: int, b: int = 0, | |
| *args: int, y: int, x: int = 1, | |
| **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: | |
| return (a, b, *args), {'x': x, 'y': y, **kwargs} | |
| class A: | |
| def f(self, x: int) -> Tuple[int, ...]: | |
| return (x,) | |
| def g(self, x: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: | |
| return (x,), {} | |
| class B(A): | |
| def f(self, *args: int) -> Tuple[int, ...]: | |
| return args | |
| def g(self, *args: int, **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: | |
| return args, kwargs | |
| [file other.py] | |
| # This file is imported in both compiled and interpreted mode in order to | |
| # test both native calls and calls via the C API. | |
| from native import ( | |
| kwonly1, kwonly2, kwonly3, kwonly4, | |
| varargs1, varargs2, varargs3, varargs4, | |
| A, B | |
| ) | |
| # kwonly arg tests | |
| assert kwonly1(10, y=20) == (10, 20) | |
| assert kwonly1(y=20) == (0, 20) | |
| assert kwonly2(x=10, y=20) == (10, 20) | |
| assert kwonly2(y=20) == (0, 20) | |
| assert kwonly3(10, y=20) == (10, 0, 1, 20) | |
| assert kwonly3(a=10, y=20) == (10, 0, 1, 20) | |
| assert kwonly3(10, 30, y=20) == (10, 30, 1, 20) | |
| assert kwonly3(10, b=30, y=20) == (10, 30, 1, 20) | |
| assert kwonly3(a=10, b=30, y=20) == (10, 30, 1, 20) | |
| assert kwonly3(10, x=40, y=20) == (10, 0, 40, 20) | |
| assert kwonly3(a=10, x=40, y=20) == (10, 0, 40, 20) | |
| assert kwonly3(10, 30, x=40, y=20) == (10, 30, 40, 20) | |
| assert kwonly3(10, b=30, x=40, y=20) == (10, 30, 40, 20) | |
| assert kwonly3(a=10, b=30, x=40, y=20) == (10, 30, 40, 20) | |
| assert kwonly4(x=1, y=2) == (1, 2) | |
| assert kwonly4(y=2, x=1) == (1, 2) | |
| # varargs tests | |
| assert varargs1() == () | |
| assert varargs1(1, 2, 3) == (1, 2, 3) | |
| assert varargs1(1, *[2, 3, 4], 5, *[6, 7, 8], 9) == (1, 2, 3, 4, 5, 6, 7, 8, 9) | |
| assert varargs2(1, 2, 3) == ((1, 2, 3), {}) | |
| assert varargs2(1, 2, 3, x=4) == ((1, 2, 3), {'x': 4}) | |
| assert varargs2(x=4) == ((), {'x': 4}) | |
| assert varargs3() == {} | |
| assert varargs3(x=4) == {'x': 4} | |
| assert varargs3(x=4, y=5) == {'x': 4, 'y': 5} | |
| assert varargs4(-1, y=2) == ((-1, 0), {'x': 1, 'y': 2}) | |
| assert varargs4(-1, 2, y=2) == ((-1, 2), {'x': 1, 'y': 2}) | |
| assert varargs4(-1, 2, 3, y=2) == ((-1, 2, 3), {'x': 1, 'y': 2}) | |
| assert varargs4(-1, 2, 3, x=10, y=2) == ((-1, 2, 3), {'x': 10, 'y': 2}) | |
| assert varargs4(-1, x=10, y=2) == ((-1, 0), {'x': 10, 'y': 2}) | |
| assert varargs4(-1, y=2, z=20) == ((-1, 0), {'x': 1, 'y': 2, 'z': 20}) | |
| assert varargs4(-1, 2, y=2, z=20) == ((-1, 2), {'x': 1, 'y': 2, 'z': 20}) | |
| assert varargs4(-1, 2, 3, y=2, z=20) == ((-1, 2, 3), {'x': 1, 'y': 2, 'z': 20}) | |
| assert varargs4(-1, 2, 3, x=10, y=2, z=20) == ((-1, 2, 3), {'x': 10, 'y': 2, 'z': 20}) | |
| assert varargs4(-1, x=10, y=2, z=20) == ((-1, 0), {'x': 10, 'y': 2, 'z': 20}) | |
| x = B() # type: A | |
| assert x.f(1) == (1,) | |
| assert x.g(1) == ((1,), {}) | |
| # This one is really funny! When we make native calls we lose | |
| # track of which arguments are positional or keyword, so the glue | |
| # calls them all positional unless they are keyword only... | |
| # It would be possible to fix this by dynamically tracking which | |
| # arguments were passed by keyword (for example, by passing a bitmask | |
| # to functions indicating this), but paying a speed, size, and complexity | |
| # cost for something so deeply marginal seems like a bad choice. | |
| # assert x.g(x=1) == ((), {'x': 1}) | |
| [file driver.py] | |
| from testutil import assertRaises | |
| from native import ( | |
| kwonly1, kwonly2, kwonly3, kwonly4, | |
| varargs1, varargs2, varargs3, varargs4, | |
| ) | |
| # Run the non-exceptional tests in both interpreted and compiled mode | |
| import other | |
| import other_interpreted | |
| # And the tests for errors at the interfaces in interpreted only | |
| with assertRaises(TypeError, "missing required keyword-only argument 'y'"): | |
| kwonly1() | |
| with assertRaises(TypeError, "takes at most 1 positional argument (2 given)"): | |
| kwonly1(10, 20) | |
| with assertRaises(TypeError, "missing required keyword-only argument 'y'"): | |
| kwonly2() | |
| with assertRaises(TypeError, "takes no positional arguments"): | |
| kwonly2(10, 20) | |
| with assertRaises(TypeError, "missing required argument 'a'"): | |
| kwonly3(b=30, x=40, y=20) | |
| with assertRaises(TypeError, "missing required keyword-only argument 'y'"): | |
| kwonly3(10) | |
| with assertRaises(TypeError, "missing required keyword-only argument 'y'"): | |
| kwonly4(x=1) | |
| with assertRaises(TypeError, "missing required keyword-only argument 'x'"): | |
| kwonly4(y=1) | |
| with assertRaises(TypeError, "missing required keyword-only argument 'x'"): | |
| kwonly4() | |
| with assertRaises(TypeError, "'x' is an invalid keyword argument for varargs1()"): | |
| varargs1(x=10) | |
| with assertRaises(TypeError, "'x' is an invalid keyword argument for varargs1()"): | |
| varargs1(1, x=10) | |
| with assertRaises(TypeError, "varargs3() takes no positional arguments"): | |
| varargs3(10) | |
| with assertRaises(TypeError, "varargs3() takes no positional arguments"): | |
| varargs3(10, x=10) | |
| with assertRaises(TypeError, "varargs4() missing required argument 'a' (pos 1)"): | |
| varargs4() | |
| with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): | |
| varargs4(1, 2) | |
| with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): | |
| varargs4(1, 2, x=1) | |
| with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): | |
| varargs4(1, 2, 3) | |
| with assertRaises(TypeError, "varargs4() missing required argument 'a' (pos 1)"): | |
| varargs4(y=20) | |
| [case testDecoratorName] | |
| def dec(f): return f | |
| @dec | |
| def foo(): pass | |
| def test_decorator_name(): | |
| assert foo.__name__ == "foo" | |
| [case testLambdaArgToOverloaded] | |
| from lib import sub | |
| def test_str_overload() -> None: | |
| assert sub('x', lambda m: m) == 'x' | |
| def test_bytes_overload() -> None: | |
| assert sub(b'x', lambda m: m) == b'x' | |
| [file lib.py] | |
| from typing import overload, Callable, TypeVar, Generic | |
| T = TypeVar("T") | |
| class Match(Generic[T]): | |
| def __init__(self, x: T) -> None: | |
| self.x = x | |
| def group(self, n: int) -> T: | |
| return self.x | |
| @overload | |
| def sub(s: str, f: Callable[[str], str]) -> str: ... | |
| @overload | |
| def sub(s: bytes, f: Callable[[bytes], bytes]) -> bytes: ... | |
| def sub(s, f): | |
| return f(s) | |
| [case testContextManagerSpecialCase] | |
| from typing import Generator, Callable, Iterator | |
| from contextlib import contextmanager | |
| @contextmanager | |
| def f() -> Iterator[None]: | |
| yield | |
| def test_special_case() -> None: | |
| a = [''] | |
| with f(): | |
| a.pop() | |
| [case testUnpackKwargsCompiled] | |
| from typing import TypedDict | |
| from typing_extensions import Unpack | |
| class Person(TypedDict): | |
| name: str | |
| age: int | |
| def foo(**kwargs: Unpack[Person]) -> None: | |
| print(kwargs["name"]) | |
| def test_unpack() -> None: | |
| # This is not really supported yet, just test that we behave reasonably. | |
| foo(name='Jennifer', age=38) | |
| [typing fixtures/typing-full.pyi] | |
| [out] | |
| Jennifer | |
| [case testNestedFunctionDunderDict312] | |
| import sys | |
| def foo() -> None: | |
| def inner() -> str: return "bar" | |
| print(inner.__dict__) # type: ignore[attr-defined] | |
| inner.__dict__.update({"x": 1}) # type: ignore[attr-defined] | |
| print(inner.__dict__) # type: ignore[attr-defined] | |
| print(inner.x) # type: ignore[attr-defined] | |
| def test_nested() -> None: | |
| if sys.version_info >= (3, 12): # type: ignore | |
| foo() | |
| [out] | |
| [out version>=3.12] | |
| {} | |
| {'x': 1} | |
| 1 | |
| [case testNestedFunctionEnvironmentIsReadOnly] | |
| from typing import Any, Callable | |
| from testutil import assertRaises | |
| def outer(value: str) -> Callable[[], str]: | |
| def inner() -> str: | |
| return value | |
| return inner | |
| def test_environment_link_is_read_only() -> None: | |
| fn: Any = outer("value") | |
| environment = fn.__mypyc_env__ | |
| with assertRaises(AttributeError): | |
| fn.__mypyc_env__ = None | |
| assert fn.__mypyc_env__ is environment | |
| assert fn() == "value" | |
| [case testFunctoolsUpdateWrapper] | |
| import functools | |
| def bar() -> None: | |
| def inner() -> str: return "bar" | |
| functools.update_wrapper(inner, bar) # type: ignore | |
| print(inner.__dict__) # type: ignore | |
| def test_update() -> None: | |
| bar() | |
| [typing fixtures/typing-full.pyi] | |
| [out] | |
| {'__module__': 'native', '__name__': 'bar', '__qualname__': 'bar', '__doc__': None, '__wrapped__': <built-in function bar>} | |
| [case testCallNestedFunctionWithNamed] | |
| def f() -> None: | |
| def a() -> None: | |
| pass | |
| def b() -> None: | |
| a() | |
| b() | |
| [file driver.py] | |
| from native import f | |
| f() | |
| [case testCallNestedFunctionWithLambda] | |
| def f(x: int) -> int: | |
| def inc(x: int) -> int: | |
| return x + 1 | |
| return (lambda x: inc(x))(1) | |
| [file driver.py] | |
| from native import f | |
| print(f(1)) | |
| [out] | |
| 2 | |
| [case testStarArgFastPaths] | |
| from typing import Any, Mapping | |
| def fn(x: str, y: int) -> str: | |
| return x * y | |
| def star_tuple(*args: Any) -> str: | |
| return fn(*args) | |
| def star_list(args: list[Any]) -> str: | |
| return fn(*args) | |
| def star_generic(args: dict[Any, Any]) -> str: | |
| return fn(*args) | |
| def star2(**kwargs: Any) -> str: | |
| return fn(**kwargs) | |
| def star2_generic(kwargs: Mapping[Any, Any]) -> str: | |
| return fn(**kwargs) | |
| def test_star_fastpath_tuple() -> None: | |
| assert star_tuple("a", 3) == "aaa" | |
| def test_star_fastpath_list() -> None: | |
| assert star_list(["a", 3]) == "aaa" | |
| def test_star_fastpath_generic() -> None: | |
| assert star_generic({"a": None, 3: None}) == "aaa" | |
| def test_star2_fastpath() -> None: | |
| assert star2(x="a", y=3) == "aaa" | |
| def test_star2_fastpath_generic() -> None: | |
| assert star2_generic({"x": "a", "y": 3}) == "aaa" | |
| [case testLambdaInListComprehension] | |
| # Lambda inside list comprehension at module level | |
| funcs_list = [lambda x, i=i: x + i for i in range(3)] | |
| def test_module_level() -> None: | |
| assert [f(10) for f in funcs_list] == [10, 11, 12] | |
| def test_in_function() -> None: | |
| funcs = [lambda x, i=i: x + i for i in range(3)] | |
| assert [f(10) for f in funcs] == [10, 11, 12] | |
| # Different parameter name than loop variable | |
| funcs2 = [lambda x, n=i: x + n for i in range(3)] | |
| assert [f(10) for f in funcs2] == [10, 11, 12] | |
| [case testLambdaInDictComprehension] | |
| # Lambda inside dict comprehension at module level | |
| funcs_dict = {k: lambda x, k=k: x + k for k in range(3)} | |
| def test_module_level() -> None: | |
| assert funcs_dict[0](10) == 10 | |
| assert funcs_dict[1](10) == 11 | |
| assert funcs_dict[2](10) == 12 | |
| def test_in_function() -> None: | |
| d = {k: (lambda k=k: k * 2) for k in range(3)} | |
| assert d[0]() == 0 | |
| assert d[1]() == 2 | |
| assert d[2]() == 4 | |
| # Different parameter name than loop variable | |
| d2 = {k: (lambda n=k: n * 2) for k in range(3)} | |
| assert d2[0]() == 0 | |
| assert d2[1]() == 2 | |
| assert d2[2]() == 4 | |
| [case testLambdaInSetComprehension] | |
| def test_set_comp() -> None: | |
| funcs = {(lambda i=i: i) for i in range(3)} | |
| results = {f() for f in funcs} | |
| assert results == {0, 1, 2} | |
| # Different parameter name than loop variable | |
| funcs2 = {(lambda n=i: n) for i in range(3)} | |
| results2 = {f() for f in funcs2} | |
| assert results2 == {0, 1, 2} | |
| [case testLambdaInComprehensionCaptureOuter] | |
| # Lambda capturing both loop var and outer function var | |
| def test_capture_outer_and_loop() -> None: | |
| base = 100 | |
| funcs = [lambda i=i: i + base for i in range(3)] | |
| assert funcs[0]() == 100 | |
| assert funcs[1]() == 101 | |
| assert funcs[2]() == 102 | |
| # Different parameter name than loop variable | |
| funcs2 = [lambda n=i: n + base for i in range(3)] | |
| assert funcs2[0]() == 100 | |
| assert funcs2[1]() == 101 | |
| assert funcs2[2]() == 102 | |
| [case testLambdaInDictComprehensionLateBind] | |
| # Dict comprehension with late-binding lambda (no default arg) | |
| def test_dict_comp_late_bind() -> None: | |
| d = {name: (lambda: name) for name in ("a", "b")} | |
| # Late binding: all lambdas see the final value of 'name' | |
| assert d["a"]() == "b" | |
| assert d["b"]() == "b" | |
| [case testLambdaInComprehensionClassLevel] | |
| # Lambda inside comprehension at class level | |
| from typing import ClassVar | |
| class Foo: | |
| A: ClassVar[object] = {name: (lambda: name) for name in ("a", "b")} | |
| [file driver.py] | |
| from native import Foo | |
| assert Foo.A["a"]() == "b" | |
| assert Foo.A["b"]() == "b" | |
| [case testLambdaInGeneratorExpression] | |
| def test_generator_with_lambda_default() -> None: | |
| result = list((lambda i=i: i * 2) for i in range(4)) | |
| assert [f() for f in result] == [0, 2, 4, 6] | |
| # Different parameter name than loop variable | |
| result2 = list((lambda n=i: n * 2) for i in range(4)) | |
| assert [f() for f in result2] == [0, 2, 4, 6] | |
| [case testLambdaInComprehensionWithParamIterable] | |
| # Lambda inside comprehension where the iterable is a function parameter. | |
| # The comprehension scope must be able to read variables from the enclosing | |
| # function scope (not just the comprehension's own env class). | |
| from typing import List, Callable | |
| def transform(items: List[int]) -> List[Callable[[], int]]: | |
| return [(lambda i=i: i * 2) for i in items] # type: ignore[misc] | |
| def transform_different_name(items: List[int]) -> List[Callable[[], int]]: | |
| return [(lambda n=i: n * 2) for i in items] # type: ignore[misc] | |
| def uses_multiple_params(items: List[str], sep: str) -> List[Callable[[], str]]: | |
| return [(lambda s=s: s + sep) for s in items] # type: ignore[misc] | |
| def uses_multiple_params_different_name(items: List[str], sep: str) -> List[Callable[[], str]]: | |
| return [(lambda t=s: t + sep) for s in items] # type: ignore[misc] | |
| def test_param_iterable() -> None: | |
| funcs = transform([1, 2, 3]) | |
| assert [f() for f in funcs] == [2, 4, 6] | |
| # Different parameter name than loop variable | |
| funcs2 = transform_different_name([1, 2, 3]) | |
| assert [f() for f in funcs2] == [2, 4, 6] | |
| def test_multiple_params() -> None: | |
| funcs = uses_multiple_params(["a", "b"], "!") | |
| assert [f() for f in funcs] == ["a!", "b!"] | |
| # Different parameter name than loop variable | |
| funcs2 = uses_multiple_params_different_name(["a", "b"], "!") | |
| assert [f() for f in funcs2] == ["a!", "b!"] | |
| [case testConcurrentFirstCallWithKeywordArgs] | |
| # Regression test for a free-threading data race in argument parser | |
| # initialization. The first call to a compiled function with keyword arguments | |
| # lazily initializes a static CPyArg_Parser and pushes it onto a global list. | |
| # When many threads race that first call on a free-threaded build, the | |
| # initialization and list insertion must be synchronized, or the runtime hits | |
| # a failed assertion (parser->next == NULL) and aborts. | |
| import sys | |
| import threading | |
| # A pool of distinct functions, none of which is called before the concurrent | |
| # test. Each function's first call lazily initializes a static CPyArg_Parser, | |
| # and the test races that initialization across threads. Keyword arguments | |
| # force the call through the slow path that runs parser_init. Using many | |
| # functions gives many independent races per run, so the test reliably triggers | |
| # the bug on an unfixed free-threaded build instead of depending on a single | |
| # narrow timing window. | |
| def g0(a: int, b: int, c: int) -> int: return a + b + c | |
| def g1(a: int, b: int, c: int) -> int: return a + b + c | |
| def g2(a: int, b: int, c: int) -> int: return a + b + c | |
| def g3(a: int, b: int, c: int) -> int: return a + b + c | |
| def g4(a: int, b: int, c: int) -> int: return a + b + c | |
| def g5(a: int, b: int, c: int) -> int: return a + b + c | |
| def g6(a: int, b: int, c: int) -> int: return a + b + c | |
| def g7(a: int, b: int, c: int) -> int: return a + b + c | |
| def g8(a: int, b: int, c: int) -> int: return a + b + c | |
| def g9(a: int, b: int, c: int) -> int: return a + b + c | |
| def g10(a: int, b: int, c: int) -> int: return a + b + c | |
| def g11(a: int, b: int, c: int) -> int: return a + b + c | |
| def g12(a: int, b: int, c: int) -> int: return a + b + c | |
| def g13(a: int, b: int, c: int) -> int: return a + b + c | |
| def g14(a: int, b: int, c: int) -> int: return a + b + c | |
| def g15(a: int, b: int, c: int) -> int: return a + b + c | |
| def g16(a: int, b: int, c: int) -> int: return a + b + c | |
| def g17(a: int, b: int, c: int) -> int: return a + b + c | |
| def g18(a: int, b: int, c: int) -> int: return a + b + c | |
| def g19(a: int, b: int, c: int) -> int: return a + b + c | |
| def g20(a: int, b: int, c: int) -> int: return a + b + c | |
| def g21(a: int, b: int, c: int) -> int: return a + b + c | |
| def g22(a: int, b: int, c: int) -> int: return a + b + c | |
| def g23(a: int, b: int, c: int) -> int: return a + b + c | |
| def g24(a: int, b: int, c: int) -> int: return a + b + c | |
| def g25(a: int, b: int, c: int) -> int: return a + b + c | |
| def g26(a: int, b: int, c: int) -> int: return a + b + c | |
| def g27(a: int, b: int, c: int) -> int: return a + b + c | |
| def g28(a: int, b: int, c: int) -> int: return a + b + c | |
| def g29(a: int, b: int, c: int) -> int: return a + b + c | |
| FUNCS = [g0, g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11, g12, g13, g14, | |
| g15, g16, g17, g18, g19, g20, g21, g22, g23, g24, g25, g26, g27, | |
| g28, g29] | |
| def is_gil_disabled() -> bool: | |
| return hasattr(sys, "_is_gil_enabled") and not sys._is_gil_enabled() | |
| def test_concurrent_first_call() -> None: | |
| if not is_gil_disabled(): | |
| # The race can only happen without the GIL. | |
| return | |
| num_threads = 16 | |
| barrier = threading.Barrier(num_threads) | |
| errors: list[str] = [] | |
| def run() -> None: | |
| # Line up all threads, then let them race freely through the list. The | |
| # first call to each function lazily initializes its parser, so every | |
| # function is a fresh race under real parallel pressure. | |
| barrier.wait() | |
| try: | |
| for fn in FUNCS: | |
| assert fn(a=1, b=2, c=3) == 6 | |
| except BaseException as e: | |
| errors.append(repr(e)) | |
| threads = [threading.Thread(target=run) for _ in range(num_threads)] | |
| for t in threads: | |
| t.start() | |
| for t in threads: | |
| t.join() | |
| assert not errors, errors |