[Python-Dev] async/await behavior on multiple calls

Guido van Rossum guido at python.org
Tue Dec 15 15:08:37 EST 2015


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 :-).

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.

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.

On Tue, Dec 15, 2015 at 11:56 AM, Roy Williams <rwilliams at lyft.com> wrote:

> Howdy,
>
> 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:
>
>
> async def echo_hi():
>     result = ''
>     echo_proc = await asyncio.create_subprocess_exec(
>             'echo', 'hello', 'world',
>             stdout=asyncio.subprocess.PIPE,
>             stderr=asyncio.subprocess.DEVNULL)
>     result = await echo_proc.stdout.read()
>     await echo_proc.wait()
>     return result
>
> async def await_twice(awaitable):
>     print('first time is {}'.format(await awaitable))
>     print('second time is {}'.format(await awaitable))
>
> loop = asyncio.get_event_loop()
> loop.run_until_complete(await_twice(echo_hi()))
>
> 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.
>
> 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.
>
> Would it make sense to shift from having `await` functions return a `
> *Future-like`* return object to returning a Future?
>
> Thanks,
> Roy
>
>
>
> _______________________________________________
> Python-Dev mailing list
> Python-Dev at python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe:
> https://mail.python.org/mailman/options/python-dev/guido%40python.org
>
>


-- 
--Guido van Rossum (python.org/~guido)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20151215/f3d75b29/attachment.html>


More information about the Python-Dev mailing list