[Python-Dev] PEP 492 vs. PEP 3152, new round

Yury Selivanov yselivanov.ml at gmail.com
Fri Apr 24 23:51:30 CEST 2015


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


More information about the Python-Dev mailing list