From 7ee6edba73b964959b68a8d308f9e8738919c29c Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 1 Jul 2026 08:07:10 +0100 Subject: [PATCH 1/2] PEP-828: add synchonous delegation recipe --- peps/pep-0828.rst | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/peps/pep-0828.rst b/peps/pep-0828.rst index a335167a292..4f34a4c29db 100644 --- a/peps/pep-0828.rst +++ b/peps/pep-0828.rst @@ -379,16 +379,46 @@ asynchronous generator methods: :meth:`~agen.asend` to :meth:`~generator.send`, :meth:`~agen.athrow` to :meth:`~generator.throw`, and :meth:`~agen.aclose` to :meth:`~generator.close`. -For example, asynchronous exceptions could be injected into synchronous -generators: +It's trivial for anyone that needs to delegate to a subgenerator to write +the wrapper class to upgrade a synchronous Iterable or Generator to an +async one before calling ``async yield from``. .. code-block:: python + class AsAsyncIterator: + def __init__(self, wrapped): + self._wrapped = iter(wrapped) + + def __aiter__(self): + return self + + async def __anext__(self): + try: + return self._wrapped.__next__() + except StopIteration as e: + raise StopAsyncIteration(e.value) from e + + + class AsAsyncGenerator(AsAsyncIterator): + async def asend(self, value): + try: + return self._wrapped.send(value) + except StopIteration as e: + raise StopAsyncIteration(e.value) from e + + async def athrow(self, exc): + try: + return self._wrapped.throw(exc) + except StopIteration as e: + raise StopAsyncIteration(e.value) from e + + async def aclose(self): + return self._wrapped.close() + + async def agen(): - async with asyncio.timeout(3): - # If the timeout fails, then an asyncio.TimeoutError would be raised - # in a *synchronous* generator! - yield from subgen() + async yield from AsAsyncIterator([1, 2, 3]) + async yield from AsAsyncGenerator(subgen()) To quote Brandt Bucher (paraphrased): From 5e6734a9fa72551c8defe067b64b8387acb60028 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 3 Jul 2026 07:49:51 +0100 Subject: [PATCH 2/2] add sphinx refs --- peps/pep-0828.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/peps/pep-0828.rst b/peps/pep-0828.rst index 4f34a4c29db..3cbddd3efd1 100644 --- a/peps/pep-0828.rst +++ b/peps/pep-0828.rst @@ -380,7 +380,8 @@ asynchronous generator methods: :meth:`~agen.asend` to :meth:`~generator.send`, to :meth:`~generator.close`. It's trivial for anyone that needs to delegate to a subgenerator to write -the wrapper class to upgrade a synchronous Iterable or Generator to an +the wrapper class to upgrade a synchronous :class:`~collections.abc.Iterable` +or :class:`~collections.abc.Generator` to an async one before calling ``async yield from``. .. code-block:: python