[Python-Dev] Combining the best of PEP 288 and PEP 325: generator exceptions and cleanup

Phillip J. Eby pje at telecommunity.com
Thu May 19 19:38:46 CEST 2005


At 10:09 AM 5/19/2005 -0700, Guido van Rossum wrote:
>[Phillip J. Eby]the only way a generator-iterator can become
> > part of a cycle is via  an action taken outside the generator.  (E.g.
> > passing it into itself via 'continue', creating a link from one of its
> > arguments to it, etc.)  So, it's probably not a terrible limitation in
> > practice.
>
>It's enough to store a reference to the generator in a global (or in
>anything that's reachable from a global). The generator's frame's
>f_globals pointer then ensures the cycle.

Well, if the generator was defined in the same module, I suppose.  This 
would probably only happen with short scripts, where the lack of GC is 
unlikely to be an issue.

However, at least it would also be possible to explicitly close the 
generator, which wasn't possible before.


>Throwing an exception also provides ample opportunity for creating
>cycles, since the frame hold a reference to the most recent traceback.
>Ironically, throwing an exception with a traceback into a generator is
>likely to cause a cycle because the traceback likely references the
>throwing frame, which certainly has a reference to the generator...

*head exploding*  Double ouch.

Wait a minute...  those cycles don't include the generator, do they?  Let 
me think.  Frame A has a reference to the generator iterator, and invokes 
throw() (directly or indirectly) on it.  Frame B, the generator frame, gets 
its f_back set to point to Frame A, but presumably that link is cleared on 
exit?  (If it isn't, it probably should be).

Anyway, frame B throws an exception, and the traceback is created.  The 
traceback has a reference to frame B.  We return to frame A, and add it to 
the traceback as well, and a reference to the traceback goes into the frame 
too.  Hm.  Still no cycle passing through the *generator iterator*, unless 
the generator's frame's f_back is still pointing to the frame that it was 
last called from.  This holds even if the generator's frame holds the 
traceback that was current at the time of the error, because that traceback 
only includes the generator's frame, not the caller's frame.

So, as long as the generator guarantees its frame's f_back is empty while 
the generator is not actually executing, the cycle should not include the 
generator, so it will still be GC'able.  (Note, by the way, that this 
property is probably another good reason for using the yield expression as 
the source location for a throw()!)



More information about the Python-Dev mailing list