<div dir="ltr"><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>I've tried to catch up with the previous threads. A summary of issues brought up:<br><br></div>1. precise syntax of `async def` (or do we need it at all)<br></div>2. do we need `async for` and `async with` (and how to spell them)<br></div>3. syntactic priority of `await`<br></div>4. `cocall` vs. `await`<br></div>5. do we really need `__aiter__` and friends<br>6. StopAsyncException<br></div><div>7. compatibility with asyncio and existing users of it<br></div><div><br></div>(I've added a few myself.)<br><br></div>I'll try to take them one by one.<br><br></div><b>1. precise syntax of `async def`<br></b></div><br>Of all the places to put `async` I still like *before* the `def` the best. I often do "imprecise search" for e.g. /def foo/ and would be unhappy if this didn't find async defs. Putting it towards the end (`def foo async()` or `def foo() async`) makes it easier to miss. A decorator makes it hard to make the syntactic distinctions required to reject `await` outside an async function. So I still prefer <b>`async def`</b>.<br><br></div><b>2. do we need `async for` and `async with`</b><br><br></div>Yes we do. Most of you are too young to remember, but once upon a time you couldn't loop over the lines of a file with a `for` loop like you do now. The amount of code that was devoted to efficiently iterate over files was tremendous. We're back in that stone age with the asyncio `StreamReader` class -- it supports `read()`, `readline()` and so on, but you can't use it with `for`, so you have to write a `while True` loop. `asyncio for` makes it possible to add a simple `__anext__` to the `StreamReader` class, as follows:<br>```<br></div>async def __anext__(self):<br></div>    line = await self.readline()<br></div>    if not line:<br>       raise StopAsyncIteration<br></div>    return line<br>```<br></div>A similar argument can be made for `async with`; the transaction commit is pretty convincing, but it also helps to be able to wait e.g. for a transport to drain upon closing a write stream. As for how to spell these, I think having `async` at the front makes it most clear that this is a special form.<br><br></div>(Though maybe we should consider `await for` and `await with`? That would have the advantage of making it easy to scan for all suspension points by searching for /await/. But being a verb it doesn't read very well.)<br><br></div><b>3. syntactic priority of `await`</b><br><br></div>Yury, could you tweak the syntax for `await` so that we can write the most common usages without parentheses? In particular I'd like to be able to write<br>```<br></div>return await foo()<br></div>with await foo() as bar: ...<br></div>foo(await bar(), await bletch())<br></div><div>```<br>(I don't care about `await foo() + await bar()` but it would be okay.)<br></div><div>```<br></div>I think this is reasonable with some tweaks of the grammar (similar to what Greg did for cocall, but without requiring call syntax at the end).<br><br></div><b>4. `cocall` vs. `await`</b><br><br></div>Python evolves. We couldn't have PEP 380 (`yield from`) without prior experience with using generators as coroutines (PEP 342), which in turn required basic generators (PEP 255), and those were a natural evolution of Python's earlier `for` loop.<br><br>We couldn't PEP  3156 (asyncio) without PEP 380 and all that came before. The asyncio library is getting plenty of adoption and it has the concept of separating the *getting* of a future[1] from *waiting* for it.  IIUC this is also how `await` works in C# (it just requires something with an async type). This has enabled a variety of operations that take futures and produce more futures.<br><br> [1] I write `future` with a lowercase 'f' to include concepts like coroutine generator objects.<br><br><b>I just can't get used to this aspect of PEP 3152, so I'm rejecting it.</b> Sorry Greg, but that's the end. We must see `await` as a refinement of `yield from`, not as an alternative. (Yury: PEP 492 is not accepted yet, but you're getting closer.)<br><br></div>One more thing: this separation is "Pythonic" in the sense that it's similar to the way *getting* a callable object is a separate act from *calling* it. While this is a cause for newbie bugs (forgetting to call an argument-less function) it has also enabled the concept of "callable" as more general and more powerful in Python: any time you need to pass a callable, you can pass e.g. a bound method or a class or something you got from `functools.partial`, and that's a useful thing (other languages require you to introduce something like a lambda in such cases, which can be painful if the thing you wrap has a complex signature -- or they don't support function parameters at all, like Java).<br><br></div><div>I know that Greg defends it by explaining that `cocal f(args)` is not a `cocall` operator applied to `f(args)`, it is the *single* operator `cocall ...(args)` applied to `f`. But this is too subtle, and it just doesn't jive with the long tradition of using `yield from f` where f is some previously obtained future.<br></div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><br><b>5. do we really need `__aiter__` and friends</b><br><br></div><div>There's a lot of added complexity, but I think it's worth it. I don't think we need to make the names longer, the 'a' prefix is fine for these methods. I think it's all in the protocols: regular `with` uses `__enter__` and `__exit__`; `async with` uses `__aenter__` and `__aexit__` (which must return futures).<br><br>Ditto for `__aiter__` and `__anext__`. I guess this means that the async equivalent to obtaining an iterator through `it = iter(xs)` followed by `for x over it` will have to look like `ait = await aiter(xs)` followed by `for x over ait`, where an iterator is required to have an `__aiter__` method that's an async function and returns self immediately. But what if you left out the `await` from the first call? I.e. can this work?<br>```<br></div><div>ait = aiter(xs)<br></div><div>async for x in ait:<br></div><div>    print(x)<br></div><div>```<br></div><div>The question here is whether the object returned by aiter(xs) has an `__aiter__` method. Since it was intended to be the target of  `await`, it has an `__await__` method. But that itself is mostly an alias for `__iter__`, not `__aiter__`. I guess it can be made to work, the object just has to implement a bunch of different protocols.<br></div><div><br><b>6. StopAsyncException</b><br><br></div><div>I'm not sure about this. The motivation given in the PEP seems to focus on the need for `__anext__` to be async. But is this really the right pattern? What if we required `ait.__anext__()` to return a future, which can either raise good old `StopIteration` or return the next value from the iteration when awaited? I'm wondering if there are a few alternatives to be explored around the async iterator protocol still.<br></div><div><br><br><b>7. compatibility with asyncio and existing users of it</b><br><br></div><div>This is just something I want to stress. On the one hand it should be really simple to take code written for pre-3.5 asyncio and rewrite it to PEP 492 -- simple change `@asyncio.coroutine` to `async def` and change `yield from` to `await`. (Everything else is optional; existing patterns for loops and context managers should continue to work unchanged, even if in some cases you may be able to simplify the code by using `async for` and `async with`.)<br><br></div><div>But it's also important that *even if you don't switch* (i.e. if you want to keep your code compatible with pre-3.5 asyncio) you can still use the PEP 492 version of asyncio -- i.e. the asyncio library that comes with 3.5 must seamlessly support mixing code that uses `await` and code that uses `yield from`. And this should go both ways -- if you have some code that uses PEP 492 and some code that uses pre-3.5 asyncio, they should be able to pass their coroutines to each other and wait for each other's coroutines.<br><br></div><div>That's all I have for now. Enjoy!<br></div><div><br>--<br><div><div><div><div><div><div><div><div><div><div><div><div><div class="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>