[Python-Dev] Fun with ExitStack

Nick Coghlan ncoghlan at gmail.com
Tue Jul 19 00:32:14 EDT 2016

On 19 July 2016 at 09:40, Barry Warsaw <barry at python.org> wrote:
> Ah, the try-finally changes the behavior!  There's probably some documentation
> somewhere that defines how a generator gets finalized, and that triggers the
> finally clause, whereas in the previous example, nothing after the yield gets
> run.  I just can't find that anything that would describe the observed
> behavior.

For the generator case, their __del__ calls self.close(), and that
throws a GeneratorExit exception into the current yield point. Since
it's an exception, that will run try/finally clauses and context
manager __exit__ methods, but otherwise bypass code after the yield

> It's all very twisty, and I'm not sure Python is doing anything wrong, but I'm
> also not sure it's *not* doing anything wrong. ;)

I think we're a bit inconsistent in how we treat the lazy cleanup for
managed resources - sometimes __del__ handles it, sometimes we
register a weakref finalizer, sometimes we don't do it at all. That
makes it hard to predict precise behaviour without knowing the
semantic details of the specific context managers involved in the

> In any case, the contextlib documentation quoted above should probably be more
> liberally sprinkled with salty caveats.  Just calling .pop_all() isn't
> necessarily enough to ensure that resources managed by an ExitStack will
> survive its garbage collection.

Aye, I'd be open to changes that clarified that even though the
ExitStack instance won't invoke any cleanup callbacks explicitly
following a pop_all(), *implicit* cleanup from its references to those
objects going away may still be triggered if you don't save the result
of the pop_all() call somewhere.


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Python-Dev mailing list