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:
                # closing
                pass
    """

    @_wraps(genfunct)
    def wrapper(*args, **kwargs):
        gen = genfunct(*args, **kwargs)
        _yieldingfrom[gen] = gen_yf = _stack()

        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, _iyf = item.iterator, None
                    try:
                        # first poll of the subiterator
                        _y = _i.next()
                    except StopIteration, _e:
                        # subiterator exhausted on first poll
                        # extract return value
                        _r = _e.args
                        if not _r: _r = (None,)
                    else:

                        # if subgenerator is another supergenerator
                        # extract the root of its gen_yf shared_stack
                        try:
                            _iyf = _i.gi_frame.f_locals['gen']
                        except (AttributeError,KeyError):
                            _iyf = []
                        else:
                            _iyf = _yieldingfrom.get(_iyf,[])
                            if isinstance(_iyf, _stack):
                                _iyf = _iyf.root()
                        gen_yf.push(_i, _iyf)

                        # INNER loop
                        while True:

                            if _iyf is None:
                                # subiterator was adopted by caller
                                # and is now exhausted
                                item = _y
                                break

                            try:
                                # yield what the subiterator did
                                _s = (yield _y)

                            except GeneratorExit, _e:
                                # close as many subiterators as possible
                                while gen_yf:
                                    _i, _iyf = gen_yf.top()
                                    try:
                                        _close = _i.close
                                    except AttributeError:
                                        pass
                                    else:
                                        _close()
                                    gen_yf.pop()
                                # finally clause will gen.close()
                                raise _e

                            except BaseException:
                                # caller did wrapper.throw
                                _x = _exc_info()
                                # throw to the subiterator if possible
                                while gen_yf:
                                    _i, _iyf = gen_yf.top()
                                    try:
                                        _throw = _i.throw
                                    except AttributeError:
                                        # doesn't attempt to close _i?
                                        # try throwing to subiterator's parent
                                        pass
                                    else:
                                        try:
                                            _y = _throw(*_x)
                                        except StopIteration, _e:
                                            _r = _e.args
                                            if not _r: _r = (None,)
                                            # drop to INTERSECTION A
                                            # then to INTERSECTION B
                                            break
                                        else:
                                            _r = None
                                            # drop to INTERSECTION A
                                            # then to INNER loop
                                            break
                                    gen_yf.pop()
                                else:
                                    # if gen raises StopIteration
                                    # or any other Exception, we propagate
                                    _y = gen.throw(*_x)
                                    _r = _iyf = None
                                    # fall through to INTERSECTION A
                                    # then to INNER loop then to OUTER loop
                                    pass

                                # INTERSECTION A
                                # restart INNER loop or proceed to B?
                                if _r is None: continue

                            # caller did send/next
                            else:
                                if not gen_yf:
                                    # subiterator was adopted by caller
                                    # and is now exhausted
                                    _r = (_s,)
                                    _iyf = None
                                    # fall through to INTERSECTION B
                                    pass
                                else:
                                    if _iyf:
                                        # check if current _i itself
                                        # now yielding from a subiterator?
                                        _i, _iyf = gen_yf.top()
                                    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 not _r: _r = (None,)
                                        # fall through to INTERSECTION B
                                        pass
                                    else:
                                        # restart INNER loop
                                        continue

                            # INTERSECTION B
                            # done yielding from active subiterator
                            # send retvalue to its parent

                            while True:
                                if _iyf is not None:
                                    gen_yf.pop()
                                if gen_yf:
                                    _i, _iyf = gen_yf.top()
                                    try:
                                        # push retval to subiterator's parent
                                        _y = _i.send(_r[0])
                                    except StopIteration, _e:
                                        # parent is exhausted, try _its_ parent
                                        _r = _e.args
                                        if not _r: _r = (None,)
                                        continue
                                    else:
                                        # fall through to INTERSECTION C
                                        # then to INNER loop
                                        pass
                                else:
                                    # gen_yf is empty, push return value to gen
                                    # if gen raises StopIteration

                                    # or any other Exception, we propagate
                                    _y = gen.send(_r[0])
                                    _iyf = None
                                    # fall through to INTERSECTION C
                                    # then to INNER loop then to OUTER loop
                                    pass

                                # INTERSECTION C
                                # passed retvalue, continue INNER 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
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:
                # closing
                pass
    
recipes/Python/576728_Pure_PythimplementatiPEP_380_yield_/recipe-576728.py