diff --git a/pep-0380.txt b/pep-0380.txt index a43562002..c923c13c0 100644 --- a/pep-0380.txt +++ b/pep-0380.txt @@ -40,8 +40,8 @@ much difficulty using a loop such as :: - for v in g: - yield v + for v in g: + yield v However, if the subgenerator is to interact properly with the caller in the case of calls to ``send()``, ``throw()`` and ``close()``, things @@ -63,7 +63,7 @@ generator: :: - yield from + yield from where is an expression evaluating to an iterable, from which an iterator is extracted. The iterator is run to exhaustion, during which @@ -78,30 +78,30 @@ becomes the value of the ``yield from`` expression. In general, the semantics can be described in terms of the iterator protocol as follows: - * Any values that the iterator yields are passed directly to the - caller. + * Any values that the iterator yields are passed directly to the + caller. - * Any values sent to the delegating generator using ``send()`` - are passed directly to the iterator. If the sent value is None, - the iterator's ``next()`` method is called. If the sent value is - not None, the iterator's ``send()`` method is called. Any exception - resulting from attempting to call ``next`` or ``send`` is raised - in the delegating generator. + * Any values sent to the delegating generator using ``send()`` + are passed directly to the iterator. If the sent value is None, + the iterator's ``next()`` method is called. If the sent value is + not None, the iterator's ``send()`` method is called. Any exception + resulting from attempting to call ``next`` or ``send`` is raised + in the delegating generator. - * Exceptions passed to the ``throw()`` method of the delegating - generator are forwarded to the ``throw()`` method of the iterator. - If the iterator does not have a ``throw()`` method, its ``close()`` - method is called if it has one, then the thrown-in exception is - raised in the delegating generator. Any exception resulting from - attempting to call these methods (apart from one case noted below) - is raised in the delegating generator. + * Exceptions passed to the ``throw()`` method of the delegating + generator are forwarded to the ``throw()`` method of the iterator. + If the iterator does not have a ``throw()`` method, its ``close()`` + method is called if it has one, then the thrown-in exception is + raised in the delegating generator. Any exception resulting from + attempting to call these methods (apart from one case noted below) + is raised in the delegating generator. - * The value of the ``yield from`` expression is the first argument - to the ``StopIteration`` exception raised by the iterator when it - terminates. + * The value of the ``yield from`` expression is the first argument + to the ``StopIteration`` exception raised by the iterator when it + terminates. - * ``return expr`` in a generator causes ``StopIteration(expr)`` to - be raised. + * ``return expr`` in a generator causes ``StopIteration(expr)`` to + be raised. Fine Details @@ -113,8 +113,9 @@ An iterator having a ``throw()`` method is expected to recognize this as a request to finalize itself. If a call to the iterator's ``throw()`` method raises a StopIteration -exception, and it is *not* the same exception object that was thrown -in, its value is returned as the value of the ``yield from`` expression +exception, and it is *not* the same exception object that was thrown in, +and the original exception was not GeneratorExit, then the value of the +new exception is returned as the value of the ``yield from`` expression and the delegating generator is resumed. @@ -133,51 +134,48 @@ Formal Semantics :: - RESULT = yield from EXPR + RESULT = yield from EXPR is semantically equivalent to :: - _i = iter(EXPR) - try: - try: - _y = _i.next() - except StopIteration, _e: - _r = _e.value - else: - while 1: - try: - _s = yield _y - except: - _m = getattr(_i, 'throw', None) - if _m is not None: - _x = sys.exc_info() - try: - _y = _m(*_x) - except StopIteration, _e: - if _e is _x[1]: - raise - else: - _r = _e.value - break - else: - _m = getattr(_i, 'close', None) - if _m is not None: - _m() - raise - else: - try: - if _s is None: - _y = _i.next() - else: - _y = _i.send(_s) - except StopIteration, _e: - _r = _e.value - break - finally: - del _i - RESULT = _r + _i = iter(EXPR) + try: + _y = _i.next() + except StopIteration, _e: + _r = _e.value + else: + while 1: + try: + _s = yield _y + except: + _m = getattr(_i, 'throw', None) + if _m is not None: + _x = sys.exc_info() + try: + _y = _m(*_x) + except StopIteration, _e: + if _e is _x[1] or isinstance(_x[1], GeneratorExit): + raise + else: + _r = _e.value + break + else: + _m = getattr(_i, 'close', None) + if _m is not None: + _m() + raise + else: + try: + if _s is None: + _y = _i.next() + else: + _y = _i.send(_s) + except StopIteration, _e: + _r = _e.value + break + RESULT = _r except that implementations are free to cache bound methods for the 'next', 'send' and 'throw' methods of the iterator upon first use. @@ -186,13 +184,13 @@ except that implementations are free to cache bound methods for the 'next', :: - return value + return value is semantically equivalent to :: - raise StopIteration(value) + raise StopIteration(value) except that, as currently, the exception cannot be caught by ``except`` clauses within the returning generator. @@ -201,14 +199,14 @@ clauses within the returning generator. :: - class StopIteration(Exception): + class StopIteration(Exception): - def __init__(self, *args): - if len(args) > 0: - self.value = args[0] - else: - self.value = None - Exception.__init__(self, *args) + def __init__(self, *args): + if len(args) > 0: + self.value = args[0] + else: + self.value = None + Exception.__init__(self, *args) Rationale @@ -249,7 +247,7 @@ so to an unfactored one in all Python implementations. The assumption made is that, in the majority of use cases, the subiterator will not be shared. The rare case of a shared subiterator can be -accommodated by means of a wrapper that blocks ``throw()`` and ``send()`` +accommodated by means of a wrapper that blocks ``throw()`` and ``close()`` calls, or by using a means other than ``yield from`` to call the subiterator. @@ -269,14 +267,14 @@ Using the proposed syntax, a statement such as :: - y = f(x) + y = f(x) where f is an ordinary function, can be transformed into a delegation call :: - y = yield from g(x) + y = yield from g(x) where g is a generator. One can reason about the behaviour of the resulting code by thinking of g as an ordinary function that can be @@ -341,13 +339,13 @@ value of the ``close()`` call to the subgenerator. However, the proposed mechanism is attractive for a couple of reasons: * Using the StopIteration exception makes it easy for other kinds - of iterators to participate in the protocol without having to - grow an extra attribute or a close() method. + of iterators to participate in the protocol without having to + grow an extra attribute or a close() method. * It simplifies the implementation, because the point at which the - return value from the subgenerator becomes available is the same - point at which StopIteration is raised. Delaying until any later - time would require storing the return value somewhere. + return value from the subgenerator becomes available is the same + point at which StopIteration is raised. Delaying until any later + time would require storing the return value somewhere. Criticisms