[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
statement.

> 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
stack.

> 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.

Cheers,
Nick.

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


More information about the Python-Dev mailing list