156 lines
4.9 KiB
Plaintext
156 lines
4.9 KiB
Plaintext
PEP: 288
|
|
Title: Generators Attributes and Exceptions
|
|
Author: Raymond Hettinger <python@rcn.com>
|
|
Status: Withdrawn
|
|
Type: Standards Track
|
|
Content-Type: text/x-rst
|
|
Created: 21-Mar-2002
|
|
Python-Version: 2.5
|
|
Post-History:
|
|
|
|
|
|
Abstract
|
|
========
|
|
|
|
This PEP proposes to enhance generators by providing mechanisms for
|
|
raising exceptions and sharing data with running generators.
|
|
|
|
|
|
Status
|
|
======
|
|
|
|
This PEP is withdrawn. The exception raising mechanism was extended
|
|
and subsumed into :pep:`343`. The attribute passing capability
|
|
never built a following, did not have a clear implementation,
|
|
and did not have a clean way for the running generator to access
|
|
its own namespace.
|
|
|
|
|
|
Rationale
|
|
=========
|
|
|
|
Currently, only class based iterators can provide attributes and
|
|
exception handling. However, class based iterators are harder to
|
|
write, less compact, less readable, and slower. A better solution
|
|
is to enable these capabilities for generators.
|
|
|
|
Enabling attribute assignments allows data to be passed to and from
|
|
running generators. The approach of sharing data using attributes
|
|
pervades Python. Other approaches exist but are somewhat hackish
|
|
in comparison.
|
|
|
|
Another evolutionary step is to add a generator method to allow
|
|
exceptions to be passed to a generator. Currently, there is no
|
|
clean method for triggering exceptions from outside the generator.
|
|
Also, generator exception passing helps mitigate the try/finally
|
|
prohibition for generators. The need is especially acute for
|
|
generators needing to flush buffers or close resources upon termination.
|
|
|
|
The two proposals are backwards compatible and require no new
|
|
keywords. They are being recommended for Python version 2.5.
|
|
|
|
|
|
|
|
Specification for Generator Attributes
|
|
======================================
|
|
|
|
Essentially, the proposal is to emulate attribute writing for classes.
|
|
The only wrinkle is that generators lack a way to refer to instances of
|
|
themselves. So, the proposal is to provide a function for discovering
|
|
the reference. For example::
|
|
|
|
def mygen(filename):
|
|
self = sys.get_generator()
|
|
myfile = open(filename)
|
|
for line in myfile:
|
|
if len(line) < 10:
|
|
continue
|
|
self.pos = myfile.tell()
|
|
yield line.upper()
|
|
|
|
g = mygen('sample.txt')
|
|
line1 = g.next()
|
|
print 'Position', g.pos
|
|
|
|
Uses for generator attributes include:
|
|
|
|
1. Providing generator clients with extra information (as shown
|
|
above).
|
|
2. Externally setting control flags governing generator operation
|
|
(possibly telling a generator when to step in or step over
|
|
data groups).
|
|
3. Writing lazy consumers with complex execution states
|
|
(an arithmetic encoder output stream for example).
|
|
4. Writing co-routines (as demonstrated in Dr. Mertz's articles [1]_).
|
|
|
|
The control flow of 'yield' and 'next' is unchanged by this
|
|
proposal. The only change is that data can passed to and from the
|
|
generator. Most of the underlying machinery is already in place,
|
|
only the access function needs to be added.
|
|
|
|
|
|
|
|
Specification for Generator Exception Passing
|
|
=============================================
|
|
|
|
Add a ``.throw(exception)`` method to the generator interface::
|
|
|
|
def logger():
|
|
start = time.time()
|
|
log = []
|
|
try:
|
|
while True:
|
|
log.append(time.time() - start)
|
|
yield log[-1]
|
|
except WriteLog:
|
|
writelog(log)
|
|
|
|
g = logger()
|
|
for i in [10,20,40,80,160]:
|
|
testsuite(i)
|
|
g.next()
|
|
g.throw(WriteLog)
|
|
|
|
There is no existing work-around for triggering an exception
|
|
inside a generator. It is the only case in Python where active
|
|
code cannot be excepted to or through.
|
|
|
|
Generator exception passing also helps address an intrinsic
|
|
limitation on generators, the prohibition against their using
|
|
try/finally to trigger clean-up code (:pep:`255`).
|
|
|
|
Note A: The name of the throw method was selected for several
|
|
reasons. Raise is a keyword and so cannot be used as a method
|
|
name. Unlike raise which immediately raises an exception from the
|
|
current execution point, throw will first return to the generator
|
|
and then raise the exception. The word throw is suggestive of
|
|
putting the exception in another location. The word throw is
|
|
already associated with exceptions in other languages.
|
|
|
|
Alternative method names were considered: ``resolve()``, ``signal()``,
|
|
``genraise()``, ``raiseinto()``, and ``flush()``. None of these fit as well
|
|
as ``throw()``.
|
|
|
|
Note B: To keep the ``throw()`` syntax simple only the instance
|
|
version of the raise syntax would be supported (no variants for
|
|
"``raise string``" or "``raise class, instance``").
|
|
|
|
Calling ``g.throw(instance)`` would correspond to writing
|
|
``raise instance`` immediately after the most recent yield.
|
|
|
|
|
|
|
|
References
|
|
==========
|
|
|
|
.. [1] Dr. David Mertz's draft columns for Charming Python
|
|
http://gnosis.cx/publish/programming/charming_python_b5.txt
|
|
http://gnosis.cx/publish/programming/charming_python_b7.txt
|
|
|
|
|
|
|
|
Copyright
|
|
=========
|
|
|
|
This document has been placed in the public domain.
|