<div dir="ltr"><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">This mostly springs off of a comment I saw in some thread.</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">The point of a with statement is that it ensures that some resource will be disposed of, yes? For example, this:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">    with open(filename) as f:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        contents = f.read()</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">is better than this:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">    contents = open(filename).read()</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">because the former definitely closes the file while the latter relies on garbage collection?</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">The point of a yield expression is to suspend execution. This is nice for efficient looping because instead of having to hold all results in memory, each result can be consumed immediately, yes? Therefore this:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">    def five_to_one():</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        for i in range(4):</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            yield 5 - i</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">is better than this:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">    def five_to_one():</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        result = []</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        for i in range(4):</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            result.append(5 - i)</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        return result</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">because the former suspends execution of "five_to_one" while the latter holds all five results in memory?</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">Now, let's take a look at the following scenario:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">    def read_multiple(*filenames):</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        for filename in filenames:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            with open(filename) as f:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">                yield f.read()</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">Can you spot the problem? The "with open(filename)" statement is supposed to ensure that the file object is disposed of properly. However, the "yield f.read()" statement suspends execution within the with block, so if this happened:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">    for contents in read_multiple('chunk1', 'chunk2', 'chunk3'):</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        if contents == 'hello':</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            break</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">and the contents of "chunk2" were "hello" then the loop would exit, and "chunk2" would never be closed! Yielding inside a with block, therefore, doesn't make sense and can only lead to obscure bugs.</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">The proper way to define the "read_multiple" function would be like so:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">    def read_multiple(*filenames):</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        for filename in filenames:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            with open(filename) as f:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">                contents = f.read()</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            yield contents</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">Save the contents in a variable somewhere, then yield the variable, instead of suspending execution within a context manager.</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">I believe all possible cases where one would yield inside a context manager can be covered by saving anything required from the context manager and then yielding the results outside. Therefore, I propose making a "yield" inside a with block become a SyntaxError.</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">This means the first "read_multiple" definition I presented will become illegal and fail <i>at compile-time</i>. However, it is still legal to define a generator inside a with block:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">    def pass_file_chars(oldfunc):</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        with open('secretfile') as f:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            contents = f.read()</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            @functools.wraps</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">            def newfunc(*args, **kwargs):</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">                for char in contents:</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">                    yield oldfunc(char, *args, **kwargs)</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">        return newfunc</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">This is probably a bad example, but I hope it still explains why it should be legal to define generators in context managers - as long as the with block serves its purpose correctly, everything else should still work normally.</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">For those concerned about backwards compatibility: I believe that those who attempt to yield inside a context manager will already discover that results are undefined when doing so; this will simply make it more obvious that suspending execution in a with block is not meant to happen, and convert undefined behavior into a straight-up SyntaxError.</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">What are your thoughts?</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff"><br></div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">Sharing,</div><div class="gmail_default" style="font-family:monospace,monospace;font-size:small;color:#0000ff">Ken Hilton;</div></div>