[Python-checkins] r85900 - peps/trunk/pep-0380.txt

guido.van.rossum python-checkins at python.org
Fri Oct 29 01:17:01 CEST 2010


Author: guido.van.rossum
Date: Fri Oct 29 01:17:00 2010
New Revision: 85900

Log:
Version 14 from Greg.


Modified:
   peps/trunk/pep-0380.txt

Modified: peps/trunk/pep-0380.txt
==============================================================================
--- peps/trunk/pep-0380.txt	(original)
+++ peps/trunk/pep-0380.txt	Fri Oct 29 01:17:00 2010
@@ -7,7 +7,7 @@
 Type: Standards Track
 Content-Type: text/x-rst
 Created: 13-Feb-2009
-Python-Version: 2.7
+Python-Version: 3.x
 Post-History:
 
 
@@ -40,8 +40,8 @@
 
 ::
 
-   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 @@
 
 ::
 
-   yield from <expr>
+    yield from <expr>
 
 where <expr> is an expression evaluating to an iterable, from which an
 iterator is extracted. The iterator is run to exhaustion, during which
@@ -71,52 +71,40 @@
 the generator containing the ``yield from`` expression (the
 "delegating generator").
 
-Furthermore, when the iterator is another generator, the subgenerator is
-allowed to execute a ``return`` statement with a value, and that value
-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 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.
-
-   * The value of the ``yield from`` expression is the first argument
-     to the ``StopIteration`` exception raised by the iterator when it
-     terminates.
+Furthermore, when the iterator is another generator, the subgenerator
+is allowed to execute a ``return`` statement with a value, and that
+value becomes the value of the ``yield from`` expression.
+
+The full semantics of the ``yield from`` expression can be described
+in terms of the generator protocol as follows:
+
+    * 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. If the call
+      raises StopIteration, the delegating generator is resumed. Any other
+      exception is propagated to the delegating generator.
+
+    * Exceptions other than GeneratorExit thrown into the delegating
+      generator are passed to the ``throw()`` method of the iterator.
+      If the call raises StopIteration, the delegating generator is resumed.
+      Any other exception is propagated to the delegating generator.
+
+    * If a GeneratorExit exception is thrown into the delegating generator,
+      or the ``close()`` method of the delegating generator is called, then
+      the ``close()`` method of the iterator is called if it has one. If this
+      call results in an exception, it is propagated to the delegating generator.
+      Otherwise, GeneratorExit 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.
 
-   * ``return expr`` in a generator causes ``StopIteration(expr)`` to
-     be raised.
-
-
-Fine Details
-------------
-
-The implicit GeneratorExit resulting from closing the delegating
-generator is treated as though it were passed in using ``throw()``.
-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,
-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.
+    * ``return expr`` in a generator causes ``StopIteration(expr)`` to
+      be raised upon exit from the generator.
 
 
 Enhancements to StopIteration
@@ -130,67 +118,70 @@
 Formal Semantics
 ----------------
 
+Python 3 syntax is used in this section.
+
 1. The statement
 
 ::
 
-   RESULT = yield from EXPR
+    RESULT = yield from EXPR
 
 is semantically equivalent to
 
 ::
 
-   _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
+    _i = iter(EXPR)
+    try:
+        _y = next(_i)
+    except StopIteration as _e:
+        _r = _e.value
+    else:
+        while 1:
+            try:
+                _s = yield _y
+            except GeneratorExit as _e:
+                try:
+                    _m = _i.close
+                except AttributeError:
+                    pass
+                else:
+                    _m()
+                raise _e
+            except BaseException as _e:
+                _x = sys.exc_info()
+                try:
+                    _m = _i.throw
+                except AttributeError:
+                    raise _e
+                else:
+                    try:
+                        _y = _m(*_x)
+                    except StopIteration as _e:
+                        _r = _e.value
+                        break
+            else:
+                try:
+                    if _s is None:
+                        _y = next(_i)
+                    else:
+                        _y = _i.send(_s)
+                except StopIteration as _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.
 
 2. In a generator, the statement
 
 ::
 
-   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.
@@ -199,14 +190,14 @@
 
 ::
 
-  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
@@ -223,13 +214,26 @@
 call the new function using a ``yield from`` expression.
 
 The behaviour of the resulting compound generator should be, as far as
-possible, exactly the same as the original unfactored generator in all
-situations, including calls to ``next()``, ``send()``, ``throw()`` and
-``close()``.
+reasonably practicable, the same as the original unfactored generator
+in all situations, including calls to ``next()``, ``send()``,
+``throw()`` and ``close()``.
 
 The semantics in cases of subiterators other than generators has been
 chosen as a reasonable generalization of the generator case.
 
+The proposed semantics have the following limitations with regard
+to refactoring:
+
+* A block of code that catches GeneratorExit without subsequently
+  re-raising it cannot be factored out while retaining exactly the
+  same behaviour.
+
+* Factored code may not behave the same way as unfactored code if a
+  StopIteration exception is thrown into the delegating generator.
+
+With use cases for these being rare to non-existent, it was not
+considered worth the extra complexity required to support them.
+
 
 Finalization
 ------------
@@ -267,14 +271,14 @@
 
 ::
 
-   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
@@ -338,16 +342,46 @@
 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.
+* Using a generalization of 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.
 
 * 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
+  point at which the exception is raised. Delaying until any later
   time would require storing the return value somewhere.
 
 
+Rejected Ideas
+--------------
+
+Some ideas were discussed but rejected.
+
+Suggestion: There should be some way to prevent the initial call to
+next(), or substitute it with a send() call with a specified value,
+the intention being to support the use of generators wrapped so that
+the initial next() is performed automatically.
+
+Resolution: Outside the scope of the proposal. Such generators should
+not be used with ``yield from``.
+
+Suggestion: If closing a subiterator raises StopIteration with a
+value, return that value from the ``close()`` call to the delegating
+generator.
+
+Resolution: Undesirable for a number of reasons. The purpose of closing
+a generator is to ensure proper cleanup, not to obtain a meaningful
+return value. Also, it would be unreliable unless the return value were
+stored so as to be available to subsequent close calls, which would
+cause it to persist for longer than expected.
+
+Suggestion: If ``close()`` is not to return a value, then raise an
+exception if StopIteration with a non-None value occurs.
+
+Resolution: No clear reason to do so. Ignoring a return value is not
+considered an error anywhere else in Python.
+
+
 Criticisms
 ==========
 
@@ -355,7 +389,8 @@
 be derived in a very different way from that of an ordinary ``yield``
 expression. This suggests that some other syntax not containing the
 word ``yield`` might be more appropriate, but no acceptable alternative
-has so far been proposed.
+has so far been proposed. Rejected alternatives include ``call``,
+``delegate`` and ``gcall``.
 
 It has been suggested that some mechanism other than ``return`` in
 the subgenerator should be used to establish the value returned by
@@ -364,21 +399,11 @@
 function, since it would not be able to return values in the same way
 as other functions.
 
-The use of an argument to StopIteration to pass the return value
-has been criticised as an "abuse of exceptions", without any
-concrete justification of this claim. In any case, this is only
-one suggested implementation; another mechanism could be used
-without losing any essential features of the proposal.
-
-It has been suggested that a different exception, such as
-GeneratorReturn, should be used instead of StopIteration to return a
-value. However, no convincing practical reason for this has been put
-forward, and the addition of a ``value`` attribute to StopIteration
-mitigates any difficulties in extracting a return value from a
-StopIteration exception that may or may not have one. Also, using a
-different exception would mean that, unlike ordinary functions,
-'return' without a value in a generator would not be equivalent to
-'return None'.
+The use of an exception to pass the return value has been criticised
+as an "abuse of exceptions", without any concrete justification of
+this claim. In any case, this is only one suggested implementation;
+another mechanism could be used without losing any essential features
+of the proposal.
 
 
 Alternative Proposals
@@ -414,12 +439,12 @@
 This document has been placed in the public domain.
 
 
-
+
 ..
-  Local Variables:
-  mode: indented-text
-  indent-tabs-mode: nil
-  sentence-end-double-space: t
-  fill-column: 70
-  coding: utf-8
-  End:
+   Local Variables:
+   mode: indented-text
+   indent-tabs-mode: nil
+   sentence-end-double-space: t
+   fill-column: 70
+   coding: utf-8
+   End:


More information about the Python-checkins mailing list