Antonio Santoro
Antonio Santoro

Reputation: 907

Yielding from a factory generated by a pytest fixture

The following pytest fixture

@pytest.fixture(scope="module")
def gen_factory():
    def _gen_factory(owner: str):

        value = ...

        yield value

        print('gen teardown')

    yield _gen_factory

yields a factory used multiple times.

In a test case, I use that fixture to create two factory and use them to produce some values:

@pytest.mark.asyncio
def test_case(gen_factory):
    gen1 = gen_factory('owner1')
    gen2 = gen_factory('owner2')
    val1 = next(gen1)
    val2 = next(gen2)

    ...

    next(gen1)
    next(gen2)

What happens is that the print('gen teardown') is called only once and then the loop is closed and the second next() call raises a StopIteration error.

What am I missing here? Why is the second print not happening?

Upvotes: 0

Views: 2282

Answers (1)

Maicon Mauricio
Maicon Mauricio

Reputation: 3001

If you don't want to change anything in the fixture, just change the last calls to next:

next(gen1, None)

With a default value it won't raise StopIteration.

An alternative is to make _gen_factory a context manager:

from contextlib import contextmanager

def gen_factory():
    @contextmanager
    def _gen_factory(owner):
        value = owner
        yield value
        print('gen teardown')
    yield _gen_factory
    

def test_case(genfactory):   
    with genfactory('owner1') as own1, genfactory('owner2') as own2:
        print(own1, own2)

The error happens on the (last but one) line having the second call to next(gen1), before it executes the last statement (next(gen2)). The iterator is exhausted. That's how generators work. StopIteration is raised at the end of the generator, (That's why the first one is printed!) once they have no items to be iterated over, unless a default value is passed to the builtin next function. StopIteration makes the for loop stop. Although generators can be used in different ways, their use is primarily to be iterated over in a for loop. That's why, by default, this 'exception' is raised.

See this tutorial on generators.

Upvotes: 4

Related Questions