# -----------------
# yield statement
# -----------------
def gen():
    if random.choice([0, 1]):
        yield 1
    else:
        yield ""

gen_exe = gen()
#? int() str()
next(gen_exe)

#? int() str() list
next(gen_exe, list)


def gen_ret(value):
    yield value

#? int()
next(gen_ret(1))

#? []
next(gen_ret()).

# generators infer to true if cast by bool.
a = ''
if gen_ret():
    a = 3
#? int()
a


# -----------------
# generators should not be indexable
# -----------------
def get(param):
    if random.choice([0, 1]):
        yield 1
    else:
        yield ""

#? []
get()[0].

# -----------------
# __iter__
# -----------------
for a in get():
    #? int() str()
    a


class Get():
    def __iter__(self):
        if random.choice([0, 1]):
            yield 1
        else:
            yield ""

b = []
for a in Get():
    #? int() str()
    a
    b += [a]

#? list()
b
#? int() str()
b[0]

g = iter(Get())
#? int() str()
next(g)

g = iter([1.0])
#? float()
next(g)

x, y = Get()
#? int() str()
x
#? int() str()
x

class Iter:
    def __iter__(self):
        yield ""
        i = 0
        while True:
            v = 1
            yield v
            i += 1
a, b, c = Iter()
#? str() int()
a
#? str() int()
b
#? str() int()
c


# -----------------
# __next__
# -----------------
class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def next(self):
        """ need to have both __next__ and next, because of py2/3 testing """
        return self.__next__()

    def __next__(self):
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1


for c in Counter(3, 8):
    #? int()
    print c


# -----------------
# tuple assignments
# -----------------
def gen():
    if random.choice([0,1]):
        yield 1, ""
    else:
        yield 2, 1.0


a, b = next(gen())
#? int()
a
#? str() float()
b


def simple():
    if random.choice([0, 1]):
        yield 1
    else:
        yield ""

a, b = simple()
#? int() str()
a
# For now this is ok.
#? int() str()
b


def simple2():
    yield 1
    yield ""

a, b = simple2()
#? int()
a
#? str()
b

a, = (a for a in [1])
#? int()
a

# -----------------
# More complicated access
# -----------------

# `close` is a method wrapper.
#? ['__call__']
gen().close.__call__

#? 
gen().throw()

#? ['co_consts']
gen().gi_code.co_consts

#? []
gen.gi_code.co_consts

# `send` is also a method wrapper.
#? ['__call__']
gen().send.__call__

#? tuple()
gen().send()

#? 
gen()()

# -----------------
# empty yield
# -----------------

def x():
    yield

#? None
next(x())
#? gen()
x()

def x():
    for i in range(3):
        yield

#? None
next(x())

# -----------------
# yield in expression
# -----------------

def x():
     a= [(yield 1)]

#? int()
next(x())

# -----------------
# statements
# -----------------
def x():
    foo = yield
    #?
    foo

# -----------------
# yield from
# -----------------

def yield_from():
    yield from iter([1])

#? int()
next(yield_from())

def yield_from_multiple():
    yield from iter([1])
    yield str()
    return 2.0

x, y = yield_from_multiple()
#? int()
x
#? str()
y

def test_nested():
    x = yield from yield_from_multiple()
    #? float()
    x
    yield x

x, y, z = test_nested()
#? int()
x
#? str()
y
# For whatever reason this is currently empty
#? float()
z


def test_in_brackets():
    x = 1 + (yield from yield_from_multiple())
    #? float()
    x

    generator = (1 for 1 in [1])
    x = yield from generator
    #? None
    x
    x = yield from 1
    #?
    x
    x = yield from [1]
    #? None
    x


# -----------------
# Annotations
# -----------------

from typing import Iterator

def annotation1() -> float:
    yield 1

def annotation2() -> Iterator[float]:
    yield 1


#?
next(annotation1())
#? float()
next(annotation2())


# annotations should override generator inference
#? float()
annotation1()