Reputation: 907
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
Reputation: 3001
next
:next(gen1, None)
With a default value it won't raise StopIteration
.
_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