Distinguishing active generators from exhausted ones

Michal Kwiatkowski constant.beta at gmail.com
Sun Jul 26 10:45:21 CEST 2009


On Jul 26, 1:10 am, Ben Finney <ben+pyt... at benfinney.id.au> wrote:
> Michal Kwiatkowski <constant.b... at gmail.com> writes:
> > I may be missing something obvious here. Is there a better way to tell
> > if a given generator object is still active or not?
>
>     foo = the_generator_object
>     try:
>         do_interesting_thing_that_needs(foo.next())
>     except StopIteration:
>         generator_is_exhausted()
>
> In other words, don't LBYL, because it's EAFP. Whatever you need to do
> that requires the next item from the generator, do that; you'll get a
> specific exception if the generator is exhausted.

The thing is I don't need the next item. I need to know if the
generator has stopped without invoking it. Why - you may ask. Well,
the answer needs some explaining.

I'm working on the Pythoscope project (http://pythoscope.org) and I
use tracing mechanisms of CPython (sys.settrace) to capture function
calls and values passed to and from them. Now, the problem with
generators is that when they are ending (i.e. returning instead of
yielding) they return a None, which is in fact indistinguishable from
"yield None". That means I can't tell if the last captured None was in
fact yielded or is a bogus value which should be rejected. Let me show
you on an example.

import sys

def trace(frame, event, arg):
    if event != 'line':
        print frame, event, arg
    return trace

def gen1():
    yield 1
    yield None

def gen2():
    yield 1

sys.settrace(trace)
print "gen1"
g1 = gen1()
g1.next()
g1.next()
print "gen2"
g2 = gen2()
[x for x in g2]
sys.settrace(None)

The first generator isn't finished, it yielded 1 and None. Second one
is exhausted after yielding a single value (1). The problem is that,
under Python 2.4 or 2.3 both invocations will generate the same trace
output. So, to know whether the last None was actually a yielded value
I need to know if a generator is active or not.

Your solution, while gives me an answer, is not acceptable because
generators can cause side effects (imagine call to launch_rockets()
before the next yield statement ;).

Cheers,
mk



More information about the Python-list mailing list