PEP 343: Remove functionality now covered under PEP 342.
This commit is contained in:
parent
f00c8f7acb
commit
5b3f4e8ce0
121
pep-0343.txt
121
pep-0343.txt
|
@ -26,19 +26,9 @@ Introduction
|
||||||
If this PEP is approved, the following PEPs will be rejected due
|
If this PEP is approved, the following PEPs will be rejected due
|
||||||
to overlap:
|
to overlap:
|
||||||
|
|
||||||
- PEP 288, Generators Attributes and Exceptions. The current PEP
|
|
||||||
covers its second half, generator exceptions (in fact the
|
|
||||||
throw() method name was taken from this PEP). I'm not in favor
|
|
||||||
of generator attributes, since they can easily be handled by
|
|
||||||
making the generator a method or by passing a mutable argument.
|
|
||||||
|
|
||||||
- PEP 310, Reliable Acquisition/Release Pairs. This is the
|
- PEP 310, Reliable Acquisition/Release Pairs. This is the
|
||||||
original with-statement proposal.
|
original with-statement proposal.
|
||||||
|
|
||||||
- PEP 325, Resource-Release Support for Generators. The current
|
|
||||||
PEP covers this (in fact the close() method name was taken from
|
|
||||||
this PEP).
|
|
||||||
|
|
||||||
- PEP 319, Python Synchronize/Asynchronize Block. Its use cases
|
- PEP 319, Python Synchronize/Asynchronize Block. Its use cases
|
||||||
can be covered by the current PEP by providing suitable
|
can be covered by the current PEP by providing suitable
|
||||||
with-statement controllers: for 'synchronize' we can use the
|
with-statement controllers: for 'synchronize' we can use the
|
||||||
|
@ -262,91 +252,11 @@ Specification: The 'with' Statement
|
||||||
non-local goto should be considered unexceptional for the purposes
|
non-local goto should be considered unexceptional for the purposes
|
||||||
of a database transaction roll-back decision.
|
of a database transaction roll-back decision.
|
||||||
|
|
||||||
Specification: Generator Enhancements
|
|
||||||
|
|
||||||
Let a generator object be the iterator produced by calling a
|
|
||||||
generator function. Below, 'g' always refers to a generator
|
|
||||||
object.
|
|
||||||
|
|
||||||
New syntax: yield allowed inside try-finally
|
|
||||||
|
|
||||||
The syntax for generator functions is extended to allow a
|
|
||||||
yield-statement inside a try-finally statement.
|
|
||||||
|
|
||||||
New generator method: throw(type, value=None, traceback=None)
|
|
||||||
|
|
||||||
g.throw(type, value, traceback) causes the specified exception to
|
|
||||||
be thrown at the point where the generator g is currently
|
|
||||||
suspended (i.e. at a yield-statement, or at the start of its
|
|
||||||
function body if next() has not been called yet). If the
|
|
||||||
generator catches the exception and yields another value, that is
|
|
||||||
the return value of g.throw(). If it doesn't catch the exception,
|
|
||||||
the throw() appears to raise the same exception passed it (it
|
|
||||||
"falls through"). If the generator raises another exception (this
|
|
||||||
includes the StopIteration produced when it returns) that
|
|
||||||
exception is raised by the throw() call. In summary, throw()
|
|
||||||
behaves like next() except it raises an exception at the
|
|
||||||
suspension point. If the generator is already in the closed
|
|
||||||
state, throw() just raises the exception it was passed without
|
|
||||||
executing any of the generator's code.
|
|
||||||
|
|
||||||
The effect of raising the exception is exactly as if the
|
|
||||||
statement:
|
|
||||||
|
|
||||||
raise type, value, traceback
|
|
||||||
|
|
||||||
was executed at the suspension point. The type argument should
|
|
||||||
not be None.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
New generator method: close()
|
|
||||||
|
|
||||||
g.close() is defined by the following pseudo-code:
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
try:
|
|
||||||
self.throw(GeneratorExit, GeneratorExit(), None)
|
|
||||||
except (GeneratorExit, StopIteration):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise RuntimeError("generator ignored GeneratorExit")
|
|
||||||
# Other exceptions are not caught
|
|
||||||
|
|
||||||
New generator method: __del__()
|
|
||||||
|
|
||||||
g.__del__() is an alias for g.close(). This will be called when
|
|
||||||
the generator object is garbage-collected (in CPython, this is
|
|
||||||
when its reference count goes to zero). If close() raises an
|
|
||||||
exception, a traceback for the exception is printed to sys.stderr
|
|
||||||
and further ignored; it is not propagated back to the place that
|
|
||||||
triggered the garbage collection. This is consistent with the
|
|
||||||
handling of exceptions in __del__() methods on class instances.
|
|
||||||
|
|
||||||
If the generator object participates in a cycle, g.__del__() may
|
|
||||||
not be called. This is the behavior of CPython's current garbage
|
|
||||||
collector. The reason for the restriction is that the GC code
|
|
||||||
needs to "break" a cycle at an arbitrary point in order to collect
|
|
||||||
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.
|
|
||||||
|
|
||||||
Generator Decorator
|
Generator Decorator
|
||||||
|
|
||||||
It is possible to write a decorator that makes it possible to use
|
If PEP 342 is accepted, it will be possible to write a decorator
|
||||||
a generator that yields exactly once to control a with-statement.
|
that makes it possible to use a generator that yields exactly once
|
||||||
Here's a sketch of such a decorator:
|
to control a with-statement. Here's a sketch of such a decorator:
|
||||||
|
|
||||||
class Wrapper(object):
|
class Wrapper(object):
|
||||||
|
|
||||||
|
@ -431,28 +341,9 @@ Open Issues
|
||||||
them here, with my preferred resolution and its motivation. The
|
them here, with my preferred resolution and its motivation. The
|
||||||
PEP as currently written reflects this preferred resolution.
|
PEP as currently written reflects this preferred resolution.
|
||||||
|
|
||||||
1. What exception should be raised by close() when the generator
|
1. The __exit__() method of the with_template decorator class catches
|
||||||
yields another value as a response to the GeneratorExit
|
StopIteration and considers it equivalent to re-raising the exception
|
||||||
exception?
|
passed to throw(). Is allowing StopIteration right here?
|
||||||
|
|
||||||
I originally chose TypeError because it represents gross
|
|
||||||
misbehavior of the generator function, which should be fixed by
|
|
||||||
changing the code. But the with_template decorator class uses
|
|
||||||
RuntimeError for similar offenses. Arguably they should all
|
|
||||||
use the same exception. I'd rather not introduce a new
|
|
||||||
exception class just for this purpose, since it's not an
|
|
||||||
exception that I want people to catch: I want it to turn into a
|
|
||||||
traceback which is seen by the programmer who then fixes the
|
|
||||||
code. So now I believe they should both raise RuntimeError.
|
|
||||||
There are some precedents for that: it's raised by the core
|
|
||||||
Python code in situations where endless recursion is detected,
|
|
||||||
and for uninitialized objects (and for a variety of
|
|
||||||
miscellaneous conditions).
|
|
||||||
|
|
||||||
2. Both the generator close() method and the __exit__() method of
|
|
||||||
the with_template decorator class catch StopIteration and
|
|
||||||
consider it equivalent to re-raising the exception passed to
|
|
||||||
throw(). Is allowing StopIteration right here?
|
|
||||||
|
|
||||||
This is so that a generator doing cleanup depending on the
|
This is so that a generator doing cleanup depending on the
|
||||||
exception thrown (like the transactional() example below) can
|
exception thrown (like the transactional() example below) can
|
||||||
|
|
Loading…
Reference in New Issue