Python etc / Generator.throw


Usually, you communicate with a generator by asking for data with next(gen). You also can send some values back with g.send(x) in Python 3. But the technique you probably don't use every day, or maybe even isn't aware of, is throwing exceptions inside a generator.

With gen.throw(e) you may raise an exception at the point where the gen generator is paused, i.e. at the point of some yield. If gen catches the exception, get.throw(e) returns the next value yielded (or StopIteration is raised). If gen doesn't catch the exception, it propagates back to you.

In : def gen():
...:     try:
...:         yield 1
...:     except ValueError:
...:         yield 2
In : g = gen()

In : next(g)
Out: 1

In : g.throw(ValueError)
Out: 2

In : g.throw(RuntimeError('TEST'))
RuntimeError: TEST

You can use it to control generator behavior more precisely, not only be sending data to it but by notifying about some problems with values yielded for example. But this is rarely required, and you have a little chance to encounter g.throw in the wild.

However, @contextmanager decorator from contextlib does exactly this to let the code inside the context catch exceptions.

In : from contextlib import contextmanager
...: @contextmanager
...: def atomic():
...:     print('BEGIN')
...:     try:
...:         yield
...:     except Exception:
...:         print('ROLLBACK')
...:     else:
...:         print('COMMIT')

In : with atomic():
...:     print('ERROR')
...:     raise RuntimeError()