<div dir="ltr">Thanks for the insight Guido.<div><br></div><div>I've mostly used async/await inside of HHVM/Hack, and used Guava/Java Futures extensively in the past so I found this behavior to be quite surprising. I'd like to use Awaitables to represent a DAG of work that needs to get done. For example, I used to be one of the maintainers of Buck (a build tool similar to Bazel) and we used a collection of futures for building all of our dependencies. For each rule, we'd effectively:</div><div><br></div><div>dependency_results = await asyncio.gather(*dependencies)<br># Proceed with building.</div><div><br></div><div>Rules were free to depend on the same dependency and since the Future would just return the same result when resolved more than one time things just worked.<br><br>Similarly when building up the results for say a web request, I effectively want to construct a DAG of work that needs to get done and then just await on that DAG in a similar manner without having to enforce that the DAG is actually a tree. I can of course write a function to wrap everything in Futures, but this seems to be against the spirit of async/await.</div><div><br></div><div>Thanks,</div><div>Roy</div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Dec 15, 2015 at 12:08 PM, Guido van Rossum <span dir="ltr"><<a href="mailto:guido@python.org" target="_blank">guido@python.org</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"><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"><div><div class="h5">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></div></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5"><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></div></div>_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org" target="_blank">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><span class="HOEnZb"><font color="#888888"><br><br clear="all"><br>-- <br><div>--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</font></span></div>
</blockquote></div><br></div></div>