<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 15 October 2017 at 20:45, Paul Moore <span dir="ltr"><<a href="mailto:p.f.moore@gmail.com" target="_blank">p.f.moore@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="gmail-HOEnZb"><div class="gmail-h5">On 15 October 2017 at 06:43, Nick Coghlan <<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>> wrote:<span class="gmail-"></span></div></div></blockquote><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="gmail-HOEnZb"><div class="gmail-h5"><span class="gmail-">>     # Generator form</span><br><span class="gmail-"></span></div></div><span class="gmail-">
>     def _results_gen(data):<br>
>         for item in data:<br>
>             with adjusted_context():<br>
>                 yield calculate_result(item)<br>
><br>
>     results = _results_gen(data)<br>
><br>
> Today, while these two forms look like they *should* be comparable, they're<br>
> not especially close to being semantically equivalent, as there's no<br>
> mechanism that allows for implicit context reversion at the yield point in<br>
> the generator form.<br>
<br>
</span>I'll have to take your word for this, as I can't think of an actual<br>
example that follows the pattern of your abstract description, for<br>
which I can immediately see the difference.<br></blockquote><div><br></div>Interestingly, thinking about the problem in terms of exception handling flow reminded me of the fact that having a generator-iterator yield while inside a with statement or try/except block is already considered an anti-pattern in many situations, precisely because it means that any exceptions that get thrown in (including GeneratorExit) will be intercepted when that may not be what the author really intended.</div><div class="gmail_quote"><br></div><div class="gmail_quote">Accordingly, the canonical guaranteed-to-be-consistent-with-the-previous-behaviour iterator -> generator transformation already involves the use of a temporary variable to move the yield outside any exception handling constructs and ensure that the exception handling only covers the code that it was originally written to cover:</div><div class="gmail_quote"><br></div><div class="gmail_quote"><span class="gmail-">    def _results_gen(data):<br>        for item in data:</span></div><span class="gmail-">            with adjusted_context():</span><br><span class="gmail-"></span><div class="gmail_quote"><span class="gmail-">                result_for_item = calculate_result(item)</span></div><div class="gmail_quote"><span class="gmail-">            yield result_for_item</span></div><div class="gmail_quote"><span class="gmail-"><br></span></div><div class="gmail_quote"><span class="gmail-">
    results = _results_gen(data)<br>
</span></div></div><div class="gmail_extra"><br></div><div class="gmail_extra">The exception handling expectations with coroutines are different, since an "await cr" expression explicitly indicates that any exceptions "cr" fails to handle *will* propagate back through to where the await appears, just as "f()" indicates that unhandled exceptions in "f" will be seen by the current frame.</div><div class="gmail_extra"><br></div><div class="gmail_extra">And even if as a new API context variables were to be defined in a yield-tolerant way, a lot of existing context managers still wouldn't be "yield safe", since they may be manipulating thread local or process global state, rather than context variables or a particular object instance.</div><br><div class="gmail_extra">Cheers,</div><div class="gmail_extra">Nick.<br></div><div class="gmail_extra"><br>-- <br><div class="gmail_signature">Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>   |   Brisbane, Australia</div>
</div></div>