<div dir="ltr"><div><div>I think this goes back all the way to a debate we had when we were discussing PEP 380 (which introduced 'yield from', on which 'await' is built). In fact I believe that the reason PEP 380 didn't make it into Python 2.7 was that this issue was unresolved at the time (the PEP author and I preferred the current approach, but there was one vocal opponent who disagreed -- although my memory is only about 60% reliable on this :-).<br><br></div>In any case, problem is that in order to implement the behavior you're asking for, the generator object would have to somehow hold on to its return value so that each time __next__ is called after it has already terminated it can raise StopIteration with the saved return value. This would extend the lifetime of the returned object indefinitely (until the generator object itself is GC'ed) in order to handle a pretty obscure corner case.<br><br></div>I don't know how long you have been using async/await, but I wonder if it's possible that you just haven't gotten used to the typical usage patterns? In particular, your claim "anything that takes an `awaitable` has to know that it wasn't already awaited" makes me sound that you're just using it in an atypical way (perhaps because your model is based on other languages). In typical asyncio code, one does not usually take an awaitable, wait for it, and then return it -- one either awaits it and then extracts the result, or one returns it without awaiting it.<br></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Dec 15, 2015 at 11:56 AM, Roy Williams <span dir="ltr"><<a href="mailto:rwilliams@lyft.com" target="_blank">rwilliams@lyft.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Howdy,<div><br></div><div>I'm experimenting with async/await in Python 3, and one very surprising behavior has been what happens when calling `await` twice on an Awaitable. In C#, Hack/HHVM, and the new async/await spec in Ecmascript 7. In Python, calling `await` multiple times results in all future results getting back `None`. Here's a small example program:<br><div><br></div><div><font face="monospace, monospace"><br></font><div><div><div><font face="monospace, monospace">async def echo_hi():</font></div><div><font face="monospace, monospace"> result = ''</font></div><div><font face="monospace, monospace"> echo_proc = await asyncio.create_subprocess_exec(</font></div><div><font face="monospace, monospace"> 'echo', 'hello', 'world',</font></div><div><font face="monospace, monospace"> stdout=asyncio.subprocess.PIPE,</font></div><div><font face="monospace, monospace"> stderr=asyncio.subprocess.DEVNULL)</font></div><div><font face="monospace, monospace"> result = await echo_proc.stdout.read()</font></div><div><font face="monospace, monospace"> await echo_proc.wait()</font></div><div><font face="monospace, monospace"> return result</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">async def await_twice(awaitable):</font></div><div><font face="monospace, monospace"> print('first time is {}'.format(await awaitable))</font></div><div><font face="monospace, monospace"> print('second time is {}'.format(await awaitable))</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">loop = asyncio.get_event_loop()</font></div><div><font face="monospace, monospace">loop.run_until_complete(await_twice(echo_hi()))</font></div></div><div><font face="monospace, monospace"><br></font></div><div>This makes writing composable APIs using async/await in Python very difficult since anything that takes an `awaitable` has to know that it wasn't already awaited. Also, since the behavior is radically different than in the other programming languages implementing async/await it makes adopting Python's flavor of async/await difficult for folks coming from a language where it's already implemented.<font face="monospace, monospace"><br></font></div><div><br></div><div>In C#/Hack/JS calls to `await` return a Task/AwaitableHandle/Promise that can be awaited multiple times and either returns the result or throws any thrown exceptions. It doesn't appear that the Awaitable class in Python has a `result` or `exception` field but `asyncio.Future` does.<br><br>Would it make sense to shift from having `await` functions return a <font face="arial, helvetica, sans-serif">`<em style="margin:0px;padding:0px;border:0px;font-stretch:inherit;line-height:28.125px;vertical-align:baseline;color:rgb(68,68,68);background-color:rgb(249,249,249)">Future-like`</em></font><em style="margin:0px;padding:0px;border:0px;font-stretch:inherit;font-size:15px;line-height:28.125px;font-family:SourceSansProItalic,Arial,sans-serif;vertical-align:baseline;color:rgb(68,68,68);background-color:rgb(249,249,249)"> </em>return object to returning a Future?</div><div><br></div><div>Thanks,</div><div>Roy</div><div><br></div><div><br></div></div></div></div></div>
<br>_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org">Python-Dev@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/guido%40python.org" rel="noreferrer" target="_blank">https://mail.python.org/mailman/options/python-dev/guido%40python.org</a><br>
<br></blockquote></div><br><br clear="all"><br>-- <br><div class="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div>