#!/usr/bin/env python3
"""Test various combinations of generators/coroutines.

This was used to cross-check the errors in the test case
testFullCoroutineMatrix in test-data/unit/check-async-await.test.
"""

import sys
from types import coroutine
from typing import Any, AsyncIterator, Awaitable, Generator, Iterator

# The various things you might try to use in `await` or `yield from`.

def plain_generator() -> Generator[str, None, int]:
    yield 'a'
    return 1

async def plain_coroutine() -> int:
    return 1

@coroutine
def decorated_generator() -> Generator[str, None, int]:
    yield 'a'
    return 1

@coroutine
async def decorated_coroutine() -> int:
    return 1

class It(Iterator[str]):
    stop = False
    def __iter__(self) -> 'It':
        return self
    def __next__(self) -> str:
        if self.stop:
            raise StopIteration('end')
        else:
            self.stop = True
            return 'a'

def other_iterator() -> It:
    return It()

class Aw(Awaitable[int]):
    def __await__(self) -> Generator[str, Any, int]:
        yield 'a'
        return 1

def other_coroutine() -> Aw:
    return Aw()

# The various contexts in which `await` or `yield from` might occur.

def plain_host_generator(func) -> Generator[str, None, None]:
    yield 'a'
    x = 0
    f = func()
    try:
        x = yield from f
    finally:
        try:
            f.close()
        except AttributeError:
            pass

async def plain_host_coroutine(func) -> None:
    x = 0
    x = await func()

@coroutine
def decorated_host_generator(func) -> Generator[str, None, None]:
    yield 'a'
    x = 0
    f = func()
    try:
        x = yield from f
    finally:
        try:
            f.close()
        except AttributeError:
            pass

@coroutine
async def decorated_host_coroutine(func) -> None:
    x = 0
    x = await func()

# Main driver.

def main():
    verbose = ('-v' in sys.argv)
    for host in [plain_host_generator, plain_host_coroutine,
                 decorated_host_generator, decorated_host_coroutine]:
        print()
        print("==== Host:", host.__name__)
        for func in [plain_generator, plain_coroutine,
                     decorated_generator, decorated_coroutine,
                     other_iterator, other_coroutine]:
            print("  ---- Func:", func.__name__)
            try:
                f = host(func)
                for i in range(10):
                    try:
                        x = f.send(None)
                        if verbose:
                            print("    yield:", x)
                    except StopIteration as e:
                        if verbose:
                            print("    stop:", e.value)
                        break
                else:
                    if verbose:
                        print("    ???? still going")
            except Exception as e:
                print("    error:", repr(e))

# Run main().

if __name__ == '__main__':
    main()
