PEP 343: Remove functionality now covered under PEP 342.

This commit is contained in:
Phillip J. Eby 2005-06-26 02:21:21 +00:00
parent f00c8f7acb
commit 5b3f4e8ce0
1 changed files with 6 additions and 115 deletions

View File

@ -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