Another major editing pass. Move __future__ into spec. Add section about compatible code.
This commit is contained in:
parent
5c96ac2382
commit
a2bb199d1d
75
pep-0479.txt
75
pep-0479.txt
|
@ -14,10 +14,12 @@ Post-History: 15-Nov-2014, 19-Nov-2014
|
||||||
Abstract
|
Abstract
|
||||||
========
|
========
|
||||||
|
|
||||||
This PEP proposes a backwards incompatible change to generators: when
|
This PEP proposes a change to generators: when ``StopIteration`` is
|
||||||
``StopIteration`` is raised inside a generator, it is replaced it with
|
raised inside a generator, it is replaced it with ``RuntimeError``.
|
||||||
``RuntimeError``. (More precisely, this happens when the exception is
|
(More precisely, this happens when the exception is about to bubble
|
||||||
about to bubble out of the generator's stack frame.)
|
out of the generator's stack frame.) Because the change is backwards
|
||||||
|
incompatible, the feature is initially introduced using a
|
||||||
|
``__future__`` statement.
|
||||||
|
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
|
@ -100,6 +102,17 @@ a generator, if the generator doesn't catch it (which it could do
|
||||||
using a ``try/except`` around the ``yield``), it will be transformed
|
using a ``try/except`` around the ``yield``), it will be transformed
|
||||||
into ``RuntimeError``.
|
into ``RuntimeError``.
|
||||||
|
|
||||||
|
During the transition phase, the new feature must be enabled
|
||||||
|
per-module using::
|
||||||
|
|
||||||
|
from __future__ import generator_stop
|
||||||
|
|
||||||
|
Any generator function constructed under the influence of this
|
||||||
|
directive will have the ``REPLACE_STOPITERATION`` flag set on its code
|
||||||
|
object, and generators with the flag set will behave according to this
|
||||||
|
proposal. Once the feature becomes standard, the flag may be dropped;
|
||||||
|
code should not inspect generators for it.
|
||||||
|
|
||||||
|
|
||||||
Consequences for existing code
|
Consequences for existing code
|
||||||
==============================
|
==============================
|
||||||
|
@ -116,28 +129,34 @@ function that terminated the generator you'd have to do "return
|
||||||
yield from helper()" rather than just "helper()".""")
|
yield from helper()" rather than just "helper()".""")
|
||||||
|
|
||||||
There are also examples of generator expressions floating around that
|
There are also examples of generator expressions floating around that
|
||||||
rely on a StopIteration raised by the expression, the target or the
|
rely on a ``StopIteration`` raised by the expression, the target or the
|
||||||
predicate (rather than by the __next__() call implied in the ``for``
|
predicate (rather than by the ``__next__()`` call implied in the ``for``
|
||||||
loop proper).
|
loop proper).
|
||||||
|
|
||||||
As this can break code, it is proposed to utilize the ``__future__``
|
Writing backwards and forwards compatible code
|
||||||
mechanism to introduce this in Python 3.5, finally making it standard
|
----------------------------------------------
|
||||||
in Python 3.6 or 3.7. The proposed syntax is::
|
|
||||||
|
|
||||||
from __future__ import generator_stop
|
With the exception of hacks that raise ``StopIteration`` to exit a
|
||||||
|
generator expression, it is easy to write code that works equally well
|
||||||
|
under older Python versions as under the new semantics.
|
||||||
|
|
||||||
Any generator function constructed under the influence of this
|
This is done by enclosing those places in the generator body where a
|
||||||
directive will have the REPLACE_STOPITERATION flag set on its code
|
``StopIteration`` is expected (e.g. bare ``next()`` calls or in some
|
||||||
object, and generators with the flag set will behave according to this
|
cases helper functions that are expected to raise ``StopIteration``)
|
||||||
proposal. Once the feature becomes standard, the flag may be dropped;
|
in a ``try/except`` construct that returns when ``StopIteration``
|
||||||
code should not inspect generators for it.
|
returns. The ``try/except`` construct should appear directly in the
|
||||||
|
generator function; doing this in a helper function that is not itself
|
||||||
|
a generator does not work. If ``raise StopIteration`` occurs directly
|
||||||
|
in a generator, simply replace it with ``return``.
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
|
|
||||||
Generators which explicitly raise StopIteration can generally be
|
Examples of breakage
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Generators which explicitly raise ``StopIteration`` can generally be
|
||||||
changed to simply return instead. This will be compatible with all
|
changed to simply return instead. This will be compatible with all
|
||||||
existing Python versions, and will not be affected by __future__.
|
existing Python versions, and will not be affected by ``__future__``.
|
||||||
|
Here are some illustrations from the standard library.
|
||||||
|
|
||||||
Lib/ipaddress.py::
|
Lib/ipaddress.py::
|
||||||
|
|
||||||
|
@ -164,7 +183,7 @@ Becomes::
|
||||||
|
|
||||||
(The ``return`` is necessary for a strictly-equivalent translation,
|
(The ``return`` is necessary for a strictly-equivalent translation,
|
||||||
though in this particular file, there is no further code, and the
|
though in this particular file, there is no further code, and the
|
||||||
``return`` can be elided.) For compatibility with pre-3.3 versions
|
``return`` can be omitted.) For compatibility with pre-3.3 versions
|
||||||
of Python, this could be written with an explicit ``for`` loop::
|
of Python, this could be written with an explicit ``for`` loop::
|
||||||
|
|
||||||
if context is None:
|
if context is None:
|
||||||
|
@ -172,10 +191,10 @@ of Python, this could be written with an explicit ``for`` loop::
|
||||||
yield line
|
yield line
|
||||||
return
|
return
|
||||||
|
|
||||||
More complicated iteration patterns will need explicit try/catch
|
More complicated iteration patterns will need explicit ``try/except``
|
||||||
constructs. For example, a parser construct like this::
|
constructs. For example, a hypothetical parser like this::
|
||||||
|
|
||||||
def unwrap(f):
|
def parser(f):
|
||||||
while True:
|
while True:
|
||||||
data = next(f)
|
data = next(f)
|
||||||
while True:
|
while True:
|
||||||
|
@ -227,7 +246,7 @@ perceived conveniences, but proper separation will make bugs more
|
||||||
visible.
|
visible.
|
||||||
|
|
||||||
An iterator is an object with a ``__next__`` method. Like many other
|
An iterator is an object with a ``__next__`` method. Like many other
|
||||||
dunder methods, it may either return a value, or raise a specific
|
special methods, it may either return a value, or raise a specific
|
||||||
exception - in this case, ``StopIteration`` - to signal that it has
|
exception - in this case, ``StopIteration`` - to signal that it has
|
||||||
no value to return. In this, it is similar to ``__getattr__`` (can
|
no value to return. In this, it is similar to ``__getattr__`` (can
|
||||||
raise ``AttributeError``), ``__getitem__`` (can raise ``KeyError``),
|
raise ``AttributeError``), ``__getitem__`` (can raise ``KeyError``),
|
||||||
|
@ -271,9 +290,9 @@ Transition plan
|
||||||
deprecation warning if ``StopIteration`` bubbles out of a generator
|
deprecation warning if ``StopIteration`` bubbles out of a generator
|
||||||
not under ``__future__`` import.
|
not under ``__future__`` import.
|
||||||
|
|
||||||
- Python 3.6: non-silent deprecation warning.
|
- Python 3.6: Non-silent deprecation warning.
|
||||||
|
|
||||||
- Python 3.7: enable new semantics everywhere.
|
- Python 3.7: Enable new semantics everywhere.
|
||||||
|
|
||||||
|
|
||||||
Alternate proposals
|
Alternate proposals
|
||||||
|
@ -363,12 +382,12 @@ The existing model has been compared to the perfectly-acceptable
|
||||||
issues inherent to every other case where an exception has special
|
issues inherent to every other case where an exception has special
|
||||||
meaning. For instance, an unexpected ``KeyError`` inside a
|
meaning. For instance, an unexpected ``KeyError`` inside a
|
||||||
``__getitem__`` method will be interpreted as failure, rather than
|
``__getitem__`` method will be interpreted as failure, rather than
|
||||||
permitted to bubble up. However, there is a difference. Dunder
|
permitted to bubble up. However, there is a difference. Special
|
||||||
methods use ``return`` to indicate normality, and ``raise`` to signal
|
methods use ``return`` to indicate normality, and ``raise`` to signal
|
||||||
abnormality; generators ``yield`` to indicate data, and ``return`` to
|
abnormality; generators ``yield`` to indicate data, and ``return`` to
|
||||||
signal the abnormal state. This makes explicitly raising
|
signal the abnormal state. This makes explicitly raising
|
||||||
``StopIteration`` entirely redundant, and potentially surprising. If
|
``StopIteration`` entirely redundant, and potentially surprising. If
|
||||||
other dunder methods had dedicated keywords to distinguish between
|
other special methods had dedicated keywords to distinguish between
|
||||||
their return paths, they too could turn unexpected exceptions into
|
their return paths, they too could turn unexpected exceptions into
|
||||||
``RuntimeError``; the fact that they cannot should not preclude
|
``RuntimeError``; the fact that they cannot should not preclude
|
||||||
generators from doing so.
|
generators from doing so.
|
||||||
|
|
Loading…
Reference in New Issue