-- Test cases for type checking mypy programs using full stubs and running
-- using CPython.
--
-- These are mostly regression tests -- no attempt is made to make these
-- complete.
--
-- This test file check Asyncio and yield from interaction

[case testImportAsyncio]
import asyncio
print('Imported')
[out]
Imported

[case testSimpleCoroutineSleep]
from typing import Any, Generator
import asyncio
from asyncio import Future

@asyncio.coroutine
def greet_every_two_seconds() -> 'Generator[Any, None, None]':
    n = 0
    while n < 5:
        print('Prev', n)
        yield from asyncio.sleep(0.1)
        print('After', n)
        n += 1

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(greet_every_two_seconds())
finally:
    loop.close()
[out]
Prev 0
After 0
Prev 1
After 1
Prev 2
After 2
Prev 3
After 3
Prev 4
After 4

[case testCoroutineCallingOtherCoroutine]
from typing import Generator, Any
import asyncio
from asyncio import Future

@asyncio.coroutine
def compute(x: int, y: int) -> 'Generator[Any, None, int]':
    print("Compute %s + %s ..." % (x, y))
    yield from asyncio.sleep(0.1)
    return x + y   # Here the int is wrapped in Future[int]

@asyncio.coroutine
def print_sum(x: int, y: int) -> 'Generator[Any, None, None]':
    result = yield from compute(x, y)  # The type of result will be int (is extracted from Future[int]
    print("%s + %s = %s" % (x, y, result))

loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
[out]
Compute 1 + 2 ...
1 + 2 = 3

[case testCoroutineChangingFuture]
from typing import Generator, Any
import asyncio
from asyncio import Future

@asyncio.coroutine
def slow_operation(future: 'Future[str]') -> 'Generator[Any, None, None]':
    yield from asyncio.sleep(0.1)
    future.set_result('Future is done!')

loop = asyncio.get_event_loop()
future = asyncio.Future()  # type: Future[str]
asyncio.Task(slow_operation(future))
loop.run_until_complete(future)
print(future.result())
loop.close()
[out]
Future is done!

[case testFunctionAssignedAsCallback]
import typing
from typing import Generator, Any
import asyncio
from asyncio import Future, AbstractEventLoop

@asyncio.coroutine
def slow_operation(future: 'Future[str]') -> 'Generator[Any, None, None]':
    yield from asyncio.sleep(1)
    future.set_result('Callback works!')

def got_result(future: 'Future[str]') -> None:
    print(future.result())
    loop.stop()

loop = asyncio.get_event_loop() # type: AbstractEventLoop
future = asyncio.Future()  # type: Future[str]
asyncio.Task(slow_operation(future))  # Here create a task with the function. (The Task need a Future[T] as first argument)
future.add_done_callback(got_result)  # and assign the callback to the future
try:
    loop.run_forever()
finally:
    loop.close()
[out]
Callback works!

[case testMultipleTasks]
import typing
from typing import Generator, Any
import asyncio
from asyncio import Task, Future
@asyncio.coroutine
def factorial(name, number) -> 'Generator[Any, None, None]':
    f = 1
    for i in range(2, number+1):
        print("Task %s: Compute factorial(%s)..." % (name, i))
        yield from asyncio.sleep(0.1)
        f *= i
    print("Task %s: factorial(%s) = %s" % (name, number, f))

loop = asyncio.get_event_loop()
tasks = [
    asyncio.Task(factorial("A", 2)),
    asyncio.Task(factorial("B", 3)),
    asyncio.Task(factorial("C", 4))]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
[out]
Task A: Compute factorial(2)...
Task B: Compute factorial(2)...
Task C: Compute factorial(2)...
Task A: factorial(2) = 2
Task B: Compute factorial(3)...
Task C: Compute factorial(3)...
Task B: factorial(3) = 6
Task C: Compute factorial(4)...
Task C: factorial(4) = 24


[case testConcatenatedCoroutines]
import typing
from typing import Generator, Any
import asyncio
from asyncio import Future

@asyncio.coroutine
def h4() -> 'Generator[Any, None, int]':
    x = yield from future
    return x

@asyncio.coroutine
def h3() -> 'Generator[Any, None, int]':
    x = yield from h4()
    print("h3: %s" % x)
    return x

@asyncio.coroutine
def h2() -> 'Generator[Any, None, int]':
    x = yield from h3()
    print("h2: %s" % x)
    return x

@asyncio.coroutine
def h() -> 'Generator[Any, None, None]':
    x = yield from h2()
    print("h: %s" % x)

loop = asyncio.get_event_loop()
future = asyncio.Future()  # type: Future[int]
future.set_result(42)
loop.run_until_complete(h())
print("Outside %s" % future.result())
loop.close()
[out]
h3: 42
h2: 42
h: 42
Outside 42

[case testConcatenatedCoroutinesReturningFutures]
import typing
from typing import Generator, Any
import asyncio
from asyncio import Future

@asyncio.coroutine
def h4() -> 'Generator[Any, None, Future[int]]':
    yield from asyncio.sleep(0.1)
    f = asyncio.Future() #type: Future[int]
    return f

@asyncio.coroutine
def h3() -> 'Generator[Any, None, Future[Future[int]]]':
    x = yield from h4()
    x.set_result(42)
    f = asyncio.Future() #type: Future[Future[int]]
    f.set_result(x)
    return f

@asyncio.coroutine
def h() -> 'Generator[Any, None, None]':
    print("Before")
    x = yield from h3()
    y = yield from x
    z = yield from y
    print(z)
    def normalize(future):
        # The str conversion seems inconsistent; not sure exactly why. Normalize
        # the result.
        return str(future).replace('<Future finished ', 'Future<')
    print(normalize(y))
    print(normalize(x))

loop = asyncio.get_event_loop()
loop.run_until_complete(h())
loop.close()
[out]
Before
42
Future<result=42>
Future<result=Future<result=42>>


[case testCoroutineWithOwnClass]
import typing
from typing import Generator, Any
import asyncio
from asyncio import Future

class A:
    def __init__(self, x: int) -> None:
        self.x = x

@asyncio.coroutine
def h() -> 'Generator[Any, None, None]':
    x = yield from future
    print("h: %s" % x.x)

loop = asyncio.get_event_loop()
future = asyncio.Future()  # type: Future[A]
future.set_result(A(42))
loop.run_until_complete(h())
print("Outside %s" % future.result().x)
loop.close()
[out]
h: 42
Outside 42


-- Errors

[case testErrorAssigningCoroutineThatDontReturn]
from typing import Generator, Any
import asyncio
from asyncio import Future

@asyncio.coroutine
def greet() -> 'Generator[Any, None, None]':
    yield from asyncio.sleep(0.2)
    print('Hello World')

@asyncio.coroutine
def test() -> 'Generator[Any, None, None]':
    yield from greet()
    x = yield from greet()  # Error

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(test())
finally:
    loop.close()
[out]
_program.py:13: error: Function does not return a value

[case testErrorReturnIsNotTheSameType]
from typing import Generator, Any
import asyncio
from asyncio import Future

@asyncio.coroutine
def compute(x: int, y: int) -> 'Generator[Any, None, int]':
    print("Compute %s + %s ..." % (x, y))
    yield from asyncio.sleep(0.1)
    return str(x + y)   # Error

@asyncio.coroutine
def print_sum(x: int, y: int) -> 'Generator[Any, None, None]':
    result = yield from compute(x, y)
    print("%s + %s = %s" % (x, y, result))

loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()

[out]
_program.py:9: error: Incompatible return value type (got "str", expected "int")

[case testErrorSetFutureDifferentInternalType]
from typing import Generator, Any
import asyncio
from asyncio import Future

@asyncio.coroutine
def slow_operation(future: 'Future[str]') -> 'Generator[Any, None, None]':
    yield from asyncio.sleep(1)
    future.set_result(42)  # Error

loop = asyncio.get_event_loop()
future = asyncio.Future()  # type: Future[str]
asyncio.Task(slow_operation(future))
loop.run_until_complete(future)
print(future.result())
loop.close()
[out]
_program.py:8: error: Argument 1 to "set_result" of "Future" has incompatible type "int"; expected "str"


[case testErrorUsingDifferentFutureType]
from typing import Any, Generator
import asyncio
from asyncio import Future

@asyncio.coroutine
def slow_operation(future: 'Future[int]') -> 'Generator[Any, None, None]':
    yield from asyncio.sleep(1)
    future.set_result(42)

loop = asyncio.get_event_loop()
future = asyncio.Future()  # type: Future[str]
asyncio.Task(slow_operation(future))  # Error
loop.run_until_complete(future)
print(future.result())
loop.close()
[out]
_program.py:12: error: Argument 1 to "slow_operation" has incompatible type "Future[str]"; expected "Future[int]"

[case testErrorUsingDifferentFutureTypeAndSetFutureDifferentInternalType]
from typing import Generator, Any
import asyncio
from asyncio import Future

asyncio.coroutine
def slow_operation(future: 'Future[int]') -> 'Generator[Any, None, None]':
    yield from asyncio.sleep(1)
    future.set_result('42')  #Try to set an str as result to a Future[int]

loop = asyncio.get_event_loop()
future = asyncio.Future()  # type: Future[str]
asyncio.Task(slow_operation(future))  # Error
loop.run_until_complete(future)
print(future.result())
loop.close()
[out]
_program.py:8: error: Argument 1 to "set_result" of "Future" has incompatible type "str"; expected "int"
_program.py:12: error: Argument 1 to "slow_operation" has incompatible type "Future[str]"; expected "Future[int]"

[case testErrorSettingCallbackWithDifferentFutureType]
import typing
from typing import Generator, Any
import asyncio
from asyncio import Future, AbstractEventLoop

@asyncio.coroutine
def slow_operation(future: 'Future[str]') -> 'Generator[Any, None, None]':
    yield from asyncio.sleep(1)
    future.set_result('Future is done!')

def got_result(future: 'Future[int]') -> None:
    print(future.result())
    loop.stop()

loop = asyncio.get_event_loop() # type: AbstractEventLoop
future = asyncio.Future()  # type: Future[str]
asyncio.Task(slow_operation(future))
future.add_done_callback(got_result)  # Error

try:
    loop.run_forever()
finally:
    loop.close()
[out]
_program.py:18: error: Argument 1 to "add_done_callback" of "Future" has incompatible type "Callable[[Future[int]], None]"; expected "Callable[[Future[str]], Any]"

[case testErrorOneMoreFutureInReturnType]
import typing
from typing import Any, Generator
import asyncio
from asyncio import Future

@asyncio.coroutine
def h4() -> 'Generator[Any, None, Future[int]]':
    yield from asyncio.sleep(1)
    f = asyncio.Future() #type: Future[int]
    return f

@asyncio.coroutine
def h3() -> 'Generator[Any, None, Future[Future[Future[int]]]]':
    x = yield from h4()
    x.set_result(42)
    f = asyncio.Future() #type: Future[Future[int]]
    f.set_result(x)
    return f

@asyncio.coroutine
def h() -> 'Generator[Any, None, None]':
    print("Before")
    x = yield from h3()
    y = yield from x
    z = yield from y
    print(z)
    print(y)
    print(x)

loop = asyncio.get_event_loop()
loop.run_until_complete(h())
loop.close()
[out]
_program.py:18: error: Incompatible return value type (got "Future[Future[int]]", expected "Future[Future[Future[int]]]")

[case testErrorOneLessFutureInReturnType]
import typing
from typing import Any, Generator
import asyncio
from asyncio import Future

@asyncio.coroutine
def h4() -> 'Generator[Any, None, Future[int]]':
    yield from asyncio.sleep(1)
    f = asyncio.Future() #type: Future[int]
    return f

@asyncio.coroutine
def h3() -> 'Generator[Any, None, Future[int]]':
    x = yield from h4()
    x.set_result(42)
    f = asyncio.Future() #type: Future[Future[int]]
    f.set_result(x)
    return f

@asyncio.coroutine
def h() -> 'Generator[Any, None, None]':
    print("Before")
    x = yield from h3()
    y = yield from x
    print(y)
    print(x)

loop = asyncio.get_event_loop()
loop.run_until_complete(h())
loop.close()
[out]
_program.py:18: error: Incompatible return value type (got "Future[Future[int]]", expected "Future[int]")

[case testErrorAssignmentDifferentType]
import typing
from typing import Generator, Any
import asyncio
from asyncio import Future

class A:
    def __init__(self, x: int) -> None:
        self.x = x

class B:
    def __init__(self, x: int) -> None:
        self.x = x

@asyncio.coroutine
def h() -> 'Generator[Any, None, None]':
    x = yield from future # type: B # Error
    print("h: %s" % x.x)

loop = asyncio.get_event_loop()
future = asyncio.Future()  # type: Future[A]
future.set_result(A(42))
loop.run_until_complete(h())
loop.close()
[out]
_program.py:16: error: Incompatible types in assignment (expression has type "A", variable has type "B")

[case testForwardRefToBadAsyncShouldNotCrash]
from typing import TypeVar
import asyncio

T = TypeVar('T')
P = whatever  # type: ignore

def test() -> None:
    reveal_type(bad)
    bad(0)

@asyncio.coroutine
def bad(arg: P) -> T:
    pass
[out]
_program.py:8: error: Revealed type is 'def [T] (arg: P?) -> T`-1'
_program.py:12: error: Invalid type "_testForwardRefToBadAsyncShouldNotCrash.P"
