[Python-ideas] PEP 525: Asynchronous Generators

Sven R. Kunze srkunze at mail.de
Thu Aug 4 09:20:42 EDT 2016


Hey Yury,

that's a great proposal!


On 03.08.2016 00:31, Yury Selivanov wrote:
>
>     class Ticker:
>         """Yield numbers from 0 to `to` every `delay` seconds."""
>
>         def __init__(self, delay, to):
>             self.delay = delay
>             self.i = 0
>             self.to = to
>
>         def __aiter__(self):
>             return self
>
>         async def __anext__(self):
>             i = self.i
>             if i >= self.to:
>                 raise StopAsyncIteration
>             self.i += 1
>             if i:
>                 await asyncio.sleep(self.delay)
>             return i
>
>
> The same can be implemented as a much simpler asynchronous generator::
>
>     async def ticker(delay, to):
>         """Yield numbers from 0 to `to` every `delay` seconds."""
>         for i in range(to):
>             yield i
>             await asyncio.sleep(delay)
>

That's a great motivational example. +1

Especially when reading the venerable PEP 255 (Q/A part), this also gets 
rid of the "low-level detail": "raise StopAsyncIteration". This could 
also be worth mentioning in the motivation part for PEP 525.


> [...]
> Asynchronous Generator Object
> -----------------------------
>
> The object is modeled after the standard Python generator object.
> Essentially, the behaviour of asynchronous generators is designed
> to replicate the behaviour of synchronous generators, with the only
> difference in that the API is asynchronous.
>
> The following methods and properties are defined:
>
> 1. ``agen.__aiter__()``: Returns ``agen``.
>
> 2. ``agen.__anext__()``: Returns an *awaitable*, that performs one
>    asynchronous generator iteration when awaited.
>
> 3. ``agen.asend(val)``: Returns an *awaitable*, that pushes the
>    ``val`` object in the ``agen`` generator.  When the ``agen`` has
>    not yet been iterated, ``val`` must be ``None``.
>
> [...]
> 4. ``agen.athrow(typ, [val, [tb]])``: Returns an *awaitable*, that
>    throws an exception into the ``agen`` generator.
>
> [...]
> 5. ``agen.aclose()``: Returns an *awaitable*, that throws a
>    ``GeneratorExit`` exception into the generator.  The *awaitable* can
>    either return a yielded value, if ``agen`` handled the exception,
>    or ``agen`` will be closed and the exception will propagate back
>    to the caller.
>
> 6. ``agen.__name__`` and ``agen.__qualname__``: readable and writable
>    name and qualified name attributes.
>
> 7. ``agen.ag_await``: The object that ``agen`` is currently *awaiting*
>    on, or ``None``.  This is similar to the currently available
>    ``gi_yieldfrom`` for generators and ``cr_await`` for coroutines.
>
> 8. ``agen.ag_frame``, ``agen.ag_running``, and ``agen.ag_code``:
>    defined in the same way as similar attributes of standard generators.
>
> ``StopIteration`` and ``StopAsyncIteration`` are not propagated out of
> asynchronous generators, and are replaced with a ``RuntimeError``.

 From your answer to Stefan, I get the impression that the reason why we 
actual need all those a* methods (basically a duplication of the 
existing gen protocol), is the fact that normal generators can be 
converted to coroutines. That means, 'yield' still can be used in both 
ways.

So, it's a technical symptom of the backwards-compatibility rather than 
something that cannot be avoided by design. Is this correct?



If it's correct, would you think it would make sense to get rid of the 
a* in a later iteration of the async capabilities of Python? So, just 
using the normal generator protocol again?


One note on all examples but the last. Reading those examples, it 
creates the illusion of actual working code which is not the case, 
right? One would always need to 1) wrap module-level statements into its 
own coroutine, 2) create an event-loop and 3) run it. Do you think 
clarifying this in the PEP makes sense?

Thanks again for your hard work here. Async generators definitely 
completes the picture.

Sven


More information about the Python-ideas mailing list