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

Copyright

This document has been placed in the public domain.

[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