PEP 342: Fix misc. typos and grammar abuses I committed while short of
sleep. :)
This commit is contained in:
parent
197f9dcbbc
commit
c97e46d034
77
pep-0342.txt
77
pep-0342.txt
|
@ -38,58 +38,58 @@ Motivation
|
|||
try/finally blocks, and therefore make it difficult for an aborted
|
||||
coroutine to clean up after itself.
|
||||
|
||||
Generators also cannot yield control while other functions are
|
||||
executing, unless those functions are also expressed as generators,
|
||||
and the outer generator is written to yield in response to values
|
||||
yielded by the inner generator, which complicates the implementation
|
||||
of even relatively simple use cases like asynchronous
|
||||
Also, generators cannot yield control while other functions are
|
||||
executing, unless those functions are themselves expressed as
|
||||
generators, and the outer generator is written to yield in response
|
||||
to values yielded by the inner generator. This complicates the
|
||||
implementation of even relatively simple use cases like asynchronous
|
||||
communications, because calling any functions either requires the
|
||||
generator to "block" (i.e. be unable to yield control), or else a
|
||||
lot of boilerplate looping code around every needed function call.
|
||||
lot of boilerplate looping code must be added around every needed
|
||||
function call.
|
||||
|
||||
However, if it were possible to pass values or exceptions into a
|
||||
However, if it were possible to pass values or exceptions *into* a
|
||||
generator at the point where it was suspended, a simple co-routine
|
||||
scheduler or "trampoline function" would let coroutines "call" each
|
||||
other without blocking -- a tremendous boon for asynchronous
|
||||
applications. Such applications could then write co-routines to
|
||||
do non-blocking socket I/O by yielding control to an I/O scheduler
|
||||
until data has been sent or becomes available. Meanwhile, code that
|
||||
performs the I/O would simply do something like:
|
||||
performs the I/O would simply do something like this:
|
||||
|
||||
data = (yield nonblocking_read(my_socket, nbytes))
|
||||
|
||||
In order to pause execution until the nonblocking_read() coroutine
|
||||
in order to pause execution until the nonblocking_read() coroutine
|
||||
produced a value.
|
||||
|
||||
In other words, with a few relatively minor enhancements to the
|
||||
language and the implementation of the generator-iterator type,
|
||||
Python will be able to support writing asynchronous applications
|
||||
without needing to write entire applications as a series of
|
||||
callbacks, and without requiring the use resource-intensive threads
|
||||
language and to the implementation of the generator-iterator type,
|
||||
Python will be able to support performing asynchronous operations
|
||||
without needing to write the entire application as a series of
|
||||
callbacks, and without requiring the use of resource-intensive threads
|
||||
for programs that need hundreds or even thousands of co-operatively
|
||||
multitasking pseudothreads. In a sense, these enhancements will
|
||||
give Python many of the benefits of the Stackless Python fork,
|
||||
multitasking pseudothreads. Thus, these enhancements will give
|
||||
standard Python many of the benefits of the Stackless Python fork,
|
||||
without requiring any significant modification to the CPython core
|
||||
or its APIs. In addition, these enhancements should be readily
|
||||
implementable by any Python implementation (such as Jython) that
|
||||
already supports generators.
|
||||
|
||||
|
||||
Specification Summary
|
||||
|
||||
By adding a few simple methods to the generator-iterator type, and
|
||||
with two minor syntax adjustments, Python developers will be able
|
||||
to use generator functions to implement co-routines and other forms
|
||||
of co-operative multitasking. These method and adjustments are:
|
||||
of co-operative multitasking. These methods and adjustments are:
|
||||
|
||||
1. Redefine "yield" to be an expression, rather than a statement.
|
||||
The current yield statement would become a yield expression
|
||||
whose value is thrown away. A yield expression's value is
|
||||
None if the generator is resumed by a normal next() call.
|
||||
None whenever the generator is resumed by a normal next() call.
|
||||
|
||||
2. Add a new send() method for generator-iterators, which resumes
|
||||
the generator and "sends" a value that becomes the result of the
|
||||
current "yield expression". The send() method returns the next
|
||||
current yield-expression. The send() method returns the next
|
||||
value yielded by the generator, or raises StopIteration if the
|
||||
generator exits without yielding another value.
|
||||
|
||||
|
@ -99,7 +99,7 @@ Specification Summary
|
|||
StopIteration if the generator exits without yielding another
|
||||
value. (If the generator does not catch the passed-in exception,
|
||||
or raises a different exception, then that exception propagates
|
||||
to the caller.
|
||||
to the caller.)
|
||||
|
||||
4. Add a close() method for generator-iterators, which raises
|
||||
GeneratorExit at the point where the generator was paused. If
|
||||
|
@ -111,12 +111,12 @@ Specification Summary
|
|||
close() does nothing if the generator has already exited due to
|
||||
an exception or normal exit.
|
||||
|
||||
5. Adding support to ensure that close() is called when a generator
|
||||
5. Add support to ensure that close() is called when a generator
|
||||
iterator is garbage-collected.
|
||||
|
||||
6. Allowing "yield" to be used in try/finally blocks, since garbage
|
||||
collection or an explicit close() call allows the finally clause
|
||||
to execute.
|
||||
6. Allow "yield" to be used in try/finally blocks, since garbage
|
||||
collection or an explicit close() call would now allow the
|
||||
finally clause to execute.
|
||||
|
||||
A prototype patch implementing all of these changes against the
|
||||
current Python CVS HEAD is available as SourceForge patch #1223381
|
||||
|
@ -140,14 +140,14 @@ Specification: Sending Values into Generators
|
|||
calling send() with a non-None argument is prohibited when the
|
||||
generator iterator has just started, and a TypeError is raised if
|
||||
this occurs (presumably due to a logic error of some kind). Thus,
|
||||
before you can communicate with a coroutine you must call first
|
||||
call next() or send(None) to advance its execution to its first
|
||||
yield expression.
|
||||
before you can communicate with a coroutine you must first call
|
||||
next() or send(None) to advance its execution to the first yield
|
||||
expression.
|
||||
|
||||
As with the next() method, the send() method returns the next value
|
||||
yielded by the generator-iterator, or raises StopIteration if the
|
||||
generator exits normally, or has already exited. If the generator
|
||||
raises an exception, it is propagated to send()'s caller.
|
||||
raises an uncaught exception, it is propagated to send()'s caller.
|
||||
|
||||
New syntax: Yield Expressions
|
||||
|
||||
|
@ -242,16 +242,16 @@ Specification: Exceptions and Cleanup
|
|||
was executed at the suspension point. The type argument must
|
||||
not be None, and the type and value must be compatible. If the
|
||||
value is not an instance of the type, a new exception instance
|
||||
is created, with the value passed in as argument(s), following
|
||||
the same rules that the raise statement uses to create an
|
||||
exception instance. The traceback, if supplied, must be a valid
|
||||
Python traceback object, or a TypeError occurs.
|
||||
is created using the value, following the same rules that the raise
|
||||
statement uses to create an exception instance. The traceback, if
|
||||
supplied, must be a valid Python traceback object, or a TypeError
|
||||
occurs.
|
||||
|
||||
New standard exception: GeneratorExit
|
||||
|
||||
A new standard exception is defined, GeneratorExit, inheriting
|
||||
from Exception. A generator should handle this by re-raising it
|
||||
or by raising StopIteration.
|
||||
(or just not catching it) or by raising StopIteration.
|
||||
|
||||
New generator method: close()
|
||||
|
||||
|
@ -283,13 +283,16 @@ Specification: Exceptions and Cleanup
|
|||
it, and from then on no Python code should be allowed to see the
|
||||
objects that formed the cycle, as they may be in an invalid state.
|
||||
Objects "hanging off" a cycle are not subject to this restriction.
|
||||
|
||||
Note that it is unlikely to see a generator object participate in
|
||||
a cycle in practice. However, storing a generator object in a
|
||||
global variable creates a cycle via the generator frame's
|
||||
f_globals pointer. Another way to create a cycle would be to
|
||||
store a reference to the generator object in a data structure that
|
||||
is passed to the generator as an argument. Neither of these cases
|
||||
are very likely given the typical pattern of generator use.
|
||||
is passed to the generator as an argument (e.g., if an object has
|
||||
a method that's a generator, and keeps a reference to a running
|
||||
iterator created by that method). Neither of these cases
|
||||
are very likely given the typical patterns of generator use.
|
||||
|
||||
Also, in the CPython implementation of this PEP, the frame object
|
||||
used by the generator should be released whenever its execution is
|
||||
|
@ -375,12 +378,12 @@ Examples
|
|||
import collections
|
||||
|
||||
class Trampoline:
|
||||
"""Manage communications between coroutines until
|
||||
"""Manage communications between coroutines"""
|
||||
|
||||
running = False
|
||||
|
||||
def __init__(self):
|
||||
self.queue = collections.deque
|
||||
self.queue = collections.deque()
|
||||
|
||||
def add(self, coroutine):
|
||||
"""Request that a coroutine be executed"""
|
||||
|
|
Loading…
Reference in New Issue