"""
Test that no StopIteration is raised inside a generator
"""
# pylint: disable=missing-docstring,invalid-name,import-error, try-except-raise, wrong-import-position,not-callable,raise-missing-from
import asyncio

class RebornStopIteration(StopIteration):
    """
    A class inheriting from StopIteration exception
    """

# This one is ok
def gen_ok():
    yield 1
    yield 2
    yield 3

# pylint should warn about this one
# because of a direct raising of StopIteration inside generator
def gen_stopiter():
    yield 1
    yield 2
    yield 3
    raise StopIteration  # [stop-iteration-return]

# pylint should warn about this one
# because of a direct raising of an exception inheriting from StopIteration inside generator
def gen_stopiterchild():
    yield 1
    yield 2
    yield 3
    raise RebornStopIteration  # [stop-iteration-return]

# pylint should warn here
# because of the possibility that next raises a StopIteration exception
def gen_next_raises_stopiter():
    g = gen_ok()
    while True:
        yield next(g)  # [stop-iteration-return]

# This one is the same as gen_next_raises_stopiter
# but is ok because the next function is inside
# a try/except block handling StopIteration
def gen_next_inside_try_except():
    g = gen_ok()
    while True:
        try:
            yield next(g)
        except StopIteration:
            return

# This one is the same as gen_next_inside_try_except
# but is not ok because the next function is inside
# a try/except block that don't handle StopIteration
def gen_next_inside_wrong_try_except():
    g = gen_ok()
    while True:
        try:
            yield next(g)  # [stop-iteration-return]
        except ValueError:
            return

# This one is the same as gen_next_inside_try_except
# but is not ok because the next function is inside
# a try/except block that handle StopIteration but reraise it
def gen_next_inside_wrong_try_except2():
    g = gen_ok()
    while True:
        try:
            yield next(g)
        except StopIteration:
            raise StopIteration  # [stop-iteration-return]

# Those two last are ok
def gen_in_for():
    for el in gen_ok():
        yield el

def gen_yield_from():
    yield from gen_ok()


def gen_dont_crash_on_no_exception():
    g = gen_ok()
    while True:
        try:
            yield next(g) # [stop-iteration-return]
        except ValueError:
            raise


def gen_dont_crash_on_uninferable():
    # https://github.com/PyCQA/pylint/issues/1779
    yield from iter()
    raise asyncio.TimeoutError()


# https://github.com/PyCQA/pylint/issues/1830
def gen_next_with_sentinel():
    yield next([], 42) # No bad return


from itertools import count

# https://github.com/PyCQA/pylint/issues/2158
def generator_using_next():
    counter = count()
    number = next(counter)
    yield number * 2


# pylint: disable=no-self-use,too-few-public-methods
class SomeClassWithNext:
    def next(self):
        return iter([1, 2, 3])
    def some_gen(self):
        for value in self.next():
            yield value


SomeClassWithNext().some_gen()


def something_invalid():
    raise Exception('cannot iterate this')


def invalid_object_passed_to_next():
    yield next(something_invalid()) # [stop-iteration-return]
