[Tutor] Yielding from a with block

Steven D'Aprano steve at pearwood.info
Fri May 29 16:59:51 CEST 2015


On Fri, May 29, 2015 at 03:01:50PM +0100, Oscar Benjamin wrote:
> On 29 May 2015 at 14:29, Steven D'Aprano <steve at pearwood.info> wrote:
> > On Thu, May 28, 2015 at 10:16:00AM +0200, Peter Otten wrote:
> >
> >> Even if you limit yourself to CPython there is another effect: the order of
> >> execution may not meet one's expectations/requirements:
> >
> > [snip example]
> >
> > That's an interesting example, and I can't tell if that's a
> > problem with your (and my) expectations, or a bug in the context
> > manager implementation.
> 
> I think that this behaviour is consistent with the generally
> unspecified language-level behaviour of deallocation and __del__.

Yes it is. There's nothing unusual going on in this example. The 
generator is still paused *inside the with block*, and it doesn't exit 
the with block until the generator is closed on deallocation, which 
might not be until interpreter exit.

[...]
> It is not guaranteed that __del__() methods are called for
> objects that still exist when the interpreter exits.

Okay, well that gives pypy a "Get Out Of Jail Free" card for the 
behaviour you demonstrated. I guess it's not necessarily a bug.


[...]
> But the with statement is not "exited" as the suite is unfinished.

Exactly! So Peter's expectation that it should be finalised is wrong. He 
managed to convince me for a few moments that there was something 
mysterious going on, but on further thought I realised that the observed 
behaviour is correct and should be expected. Escaping from the "for x in 
generator" loop early leaves the generator paused inside the with 
block.


> The
> generator has suspended itself and removed itself from the call-stack
> so that it is no longer permitted to catch exceptions etc. Also note
> that the removal from the call stack is not an implementation detail.
> The interpreter can implement the call stack how it likes but it must
> be semantically equivalent to a call stack with exception unwinding in
> order to meet the general definition of the language.

The documention of with statements and context managers says nothing 
about the call stack, and nor should it, since the call stack is part of 
the implementation of the Python virtual machine, not part of the Python 
runtime. What's important is not *how* a paused generator ceases to 
catch exceptions etc. but the fact that it no longer does. That's what I 
mean by calling it an implementation detail.

-- 
Steve


More information about the Tutor mailing list