Lukasz, On 2015-04-24 5:37 PM, Łukasz Langa wrote:
(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.) I’m on the fence here.
OT1H, I think “await for something in a_container” and “await with a_context_manager():” also read pretty well. It’s also more consistent with being the one way of finding “yield points”.
OTOH, “await with a_context_manager():” suggests the entire statement is awaited on, which is not true. “async with” is more opaque in this way, it simply states “there are going to be implementation-specific awaits inside”. So it’s more consistent with “async def foo()”.
This. I also think of 'await with' as I'm awaiting on the whole statement. And I don't even know how to interpret that.
6. StopAsyncException
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. So are you suggesting to pass the returned value in a future? In this case the future would need to be passed to __anext__, so the Cursor example from the PEP would look like this:
class Cursor: def __init__(self): self.buffer = collections.deque()
def _prefetch(self): ...
async def __aiter__(self): return self
async def __anext__(self, fut): if not self.buffer: self.buffer = await self._prefetch() if self.buffer: fut.set_result(self.buffer.popleft()) else: fut.set_exception(StopIteration)
While this is elegant, my concern is that one-future-per-iteration-step might be bad for performance.
Maybe consider the following. The `async def` syntax decouples the concept of a coroutine from the implementation. While it’s still based on generators under the hood, the user no longer considers his “async function” to be a generator or conforming to the generator protocol. From the user’s perpective, it’s obvious that the return below means something different than the exception:
async def __anext__(self): if not self.buffer: self.buffer = await self._prefetch() if not self.buffer: raise StopIteration return self.buffer.popleft()
So, the same way we added wrapping in RuntimeErrors for generators in PEP 479, we might add transparent wrapping in a _StopAsyncIteration for CO_COROUTINE.
FWIW I have to experiment more with the reference implementation, but at the moment I'm big -1 on touching StopIteration for coroutines. It's used for too many things. Thanks! Yury