from compat.functools import wraps as _wraps from sys import exc_info as _exc_info class _from(object): def __init__(self, EXPR): self.iterator = iter(EXPR) def supergenerator(genfunct): """Implements PEP 380. Use as: @supergenerator def genfunct(*args): try: sent1 = (yield val1) ,,, retval = yield _from(iterator) ... except Exception, e: # caller did generator.throw pass finally: pass # closing """ @_wraps(genfunct) def wrapper(*args, **kwargs): gen = genfunct(*args, **kwargs) try: # if first poll of gen raises StopIteration # or any other Exception, we propagate item = gen.next() # OUTER loop while True: # yield _from(EXPR) # semantics based on PEP 380, Revised**12, 19 April if isinstance(item, _from): _i = item.iterator try: # first poll of the subiterator _y = _i.next() except StopIteration, _e: # subiterator exhausted on first poll # extract return value _r = _e.args if _e.args else (None,) else: # INNER loop while True: try: # yield what the subiterator did _s = (yield _y) except GeneratorExit, _e: # close the subiterator if possible try: _close = _i.close except AttributeError: pass else: _close() # finally clause will gen.close() raise _e except BaseException: # caller did wrapper.throw _x = _exc_info() # throw to the subiterator if possible try: _throw = _i.throw except AttributeError: # doesn't attempt to close _i? # if gen raises StopIteration # or any other Exception, we propagate item = gen.throw(*_x) _r = None # fall through to INTERSECTION A # then to OUTER loop pass else: try: _y = _throw(*_x) except StopIteration, _e: _r = _e.args if _e.args else (None,) # fall through to INTERSECTION A # then to INTERSECTION B pass else: # restart INNER loop continue # INTERSECTION A # restart OUTER loop or proceed to B? if _r is None: break else: try: # re-poll the subiterator if _s is None: _y = _i.next() else: _y = _i.send(_s) except StopIteration, _e: # subiterator is exhausted # extract return value _r = _e.args if _e.args else (None,) # fall through to INTERSECTION B pass else: # restart INNER loop continue # INTERSECTION B # done yielding from subiterator # send retvalue to gen # if gen raises StopIteration # or any other Exception, we propagate item = gen.send(_r[0]) # restart OUTER loop break # traditional yield from gen else: try: sent = (yield item) except Exception: # caller did wrapper.throw _x = _exc_info() # if gen raises StopIteration # or any other Exception, we propagate item = gen.throw(*_x) else: # if gen raises StopIteration # or any other Exception, we propagate item = gen.send(sent) # end of OUTER loop, restart it pass finally: # gen raised Exception # or caller did wrapper.close() # or wrapper was garbage collected gen.close() return wrapper