summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYury Selivanov <yury@magic.io>2016-11-16 23:25:04 (GMT)
committerYury Selivanov <yury@magic.io>2016-11-16 23:25:04 (GMT)
commit22214ab0af2cbea1611b2193354f248ca6b03e87 (patch)
treef7cf45d0205d2dca9f0021f01a1551de395f370e
parent41782e497092a27efbae20208ce7d48c657e74cb (diff)
downloadcpython-22214ab0af2cbea1611b2193354f248ca6b03e87.zip
cpython-22214ab0af2cbea1611b2193354f248ca6b03e87.tar.gz
cpython-22214ab0af2cbea1611b2193354f248ca6b03e87.tar.bz2
Issue #28720: Add collections.abc.AsyncGenerator.
-rw-r--r--Doc/library/collections.abc.rst8
-rw-r--r--Doc/whatsnew/3.6.rst4
-rw-r--r--Lib/_collections_abc.py59
-rw-r--r--Lib/test/test_collections.py84
-rw-r--r--Misc/NEWS2
5 files changed, 155 insertions, 2 deletions
diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst
index e4b75a0..3ac49db 100644
--- a/Doc/library/collections.abc.rst
+++ b/Doc/library/collections.abc.rst
@@ -92,6 +92,7 @@ ABC Inherits from Abstract Methods Mixin
:class:`Coroutine` :class:`Awaitable` ``send``, ``throw`` ``close``
:class:`AsyncIterable` ``__aiter__``
:class:`AsyncIterator` :class:`AsyncIterable` ``__anext__`` ``__aiter__``
+:class:`AsyncGenerator` :class:`AsyncIterator` ``asend``, ``athrow`` ``aclose``, ``__aiter__``, ``__anext__``
========================== ====================== ======================= ====================================================
@@ -222,6 +223,13 @@ ABC Inherits from Abstract Methods Mixin
.. versionadded:: 3.5
+.. class:: Generator
+
+ ABC for asynchronous generator classes that implement the protocol
+ defined in :pep:`525` and :pep:`492`.
+
+ .. versionadded:: 3.6
+
These ABCs allow us to ask classes or instances if they provide
particular functionality, for example::
diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst
index 801191c..ce8ab98 100644
--- a/Doc/whatsnew/3.6.rst
+++ b/Doc/whatsnew/3.6.rst
@@ -912,6 +912,10 @@ The new :class:`~collections.abc.Reversible` abstract base class represents
iterable classes that also provide the :meth:`__reversed__`.
(Contributed by Ivan Levkivskyi in :issue:`25987`.)
+The new :class:`~collections.abc.AsyncGenerator` abstract base class represents
+asynchronous generators.
+(Contributed by Yury Selivanov in :issue:`28720`.)
+
The :func:`~collections.namedtuple` function now accepts an optional
keyword argument *module*, which, when specified, is used for
the ``__module__`` attribute of the returned named tuple class.
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index ee7d0f1..b172f3f 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -9,7 +9,8 @@ Unit tests are in test_collections.
from abc import ABCMeta, abstractmethod
import sys
-__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
+__all__ = ["Awaitable", "Coroutine",
+ "AsyncIterable", "AsyncIterator", "AsyncGenerator",
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
"Sized", "Container", "Callable", "Collection",
"Set", "MutableSet",
@@ -59,6 +60,11 @@ _coro = _coro()
coroutine = type(_coro)
_coro.close() # Prevent ResourceWarning
del _coro
+## asynchronous generator ##
+async def _ag(): yield
+_ag = _ag()
+async_generator = type(_ag)
+del _ag
### ONE-TRICK PONIES ###
@@ -183,6 +189,57 @@ class AsyncIterator(AsyncIterable):
return NotImplemented
+class AsyncGenerator(AsyncIterator):
+
+ __slots__ = ()
+
+ async def __anext__(self):
+ """Return the next item from the asynchronous generator.
+ When exhausted, raise StopAsyncIteration.
+ """
+ return await self.asend(None)
+
+ @abstractmethod
+ async def asend(self, value):
+ """Send a value into the asynchronous generator.
+ Return next yielded value or raise StopAsyncIteration.
+ """
+ raise StopAsyncIteration
+
+ @abstractmethod
+ async def athrow(self, typ, val=None, tb=None):
+ """Raise an exception in the asynchronous generator.
+ Return next yielded value or raise StopAsyncIteration.
+ """
+ if val is None:
+ if tb is None:
+ raise typ
+ val = typ()
+ if tb is not None:
+ val = val.with_traceback(tb)
+ raise val
+
+ async def aclose(self):
+ """Raise GeneratorExit inside coroutine.
+ """
+ try:
+ await self.athrow(GeneratorExit)
+ except (GeneratorExit, StopAsyncIteration):
+ pass
+ else:
+ raise RuntimeError("asynchronous generator ignored GeneratorExit")
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is AsyncGenerator:
+ return _check_methods(C, '__aiter__', '__anext__',
+ 'asend', 'athrow', 'aclose')
+ return NotImplemented
+
+
+AsyncGenerator.register(async_generator)
+
+
class Iterable(metaclass=ABCMeta):
__slots__ = ()
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 52ff256..87454cc 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -19,7 +19,8 @@ from collections import namedtuple, Counter, OrderedDict, _count_elements
from collections import UserDict, UserString, UserList
from collections import ChainMap
from collections import deque
-from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable
+from collections.abc import Awaitable, Coroutine
+from collections.abc import AsyncIterator, AsyncIterable, AsyncGenerator
from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible
from collections.abc import Sized, Container, Callable, Collection
from collections.abc import Set, MutableSet
@@ -959,6 +960,87 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.assertRaises(RuntimeError, IgnoreGeneratorExit().close)
+ def test_AsyncGenerator(self):
+ class NonAGen1:
+ def __aiter__(self): return self
+ def __anext__(self): return None
+ def aclose(self): pass
+ def athrow(self, typ, val=None, tb=None): pass
+
+ class NonAGen2:
+ def __aiter__(self): return self
+ def __anext__(self): return None
+ def aclose(self): pass
+ def asend(self, value): return value
+
+ class NonAGen3:
+ def aclose(self): pass
+ def asend(self, value): return value
+ def athrow(self, typ, val=None, tb=None): pass
+
+ non_samples = [
+ None, 42, 3.14, 1j, b"", "", (), [], {}, set(),
+ iter(()), iter([]), NonAGen1(), NonAGen2(), NonAGen3()]
+ for x in non_samples:
+ self.assertNotIsInstance(x, AsyncGenerator)
+ self.assertFalse(issubclass(type(x), AsyncGenerator), repr(type(x)))
+
+ class Gen:
+ def __aiter__(self): return self
+ async def __anext__(self): return None
+ async def aclose(self): pass
+ async def asend(self, value): return value
+ async def athrow(self, typ, val=None, tb=None): pass
+
+ class MinimalAGen(AsyncGenerator):
+ async def asend(self, value):
+ return value
+ async def athrow(self, typ, val=None, tb=None):
+ await super().athrow(typ, val, tb)
+
+ async def gen():
+ yield 1
+
+ samples = [gen(), Gen(), MinimalAGen()]
+ for x in samples:
+ self.assertIsInstance(x, AsyncIterator)
+ self.assertIsInstance(x, AsyncGenerator)
+ self.assertTrue(issubclass(type(x), AsyncGenerator), repr(type(x)))
+ self.validate_abstract_methods(AsyncGenerator, 'asend', 'athrow')
+
+ def run_async(coro):
+ result = None
+ while True:
+ try:
+ coro.send(None)
+ except StopIteration as ex:
+ result = ex.args[0] if ex.args else None
+ break
+ return result
+
+ # mixin tests
+ mgen = MinimalAGen()
+ self.assertIs(mgen, mgen.__aiter__())
+ self.assertIs(run_async(mgen.asend(None)), run_async(mgen.__anext__()))
+ self.assertEqual(2, run_async(mgen.asend(2)))
+ self.assertIsNone(run_async(mgen.aclose()))
+ with self.assertRaises(ValueError):
+ run_async(mgen.athrow(ValueError))
+
+ class FailOnClose(AsyncGenerator):
+ async def asend(self, value): return value
+ async def athrow(self, *args): raise ValueError
+
+ with self.assertRaises(ValueError):
+ run_async(FailOnClose().aclose())
+
+ class IgnoreGeneratorExit(AsyncGenerator):
+ async def asend(self, value): return value
+ async def athrow(self, *args): pass
+
+ with self.assertRaises(RuntimeError):
+ run_async(IgnoreGeneratorExit().aclose())
+
def test_Sized(self):
non_samples = [None, 42, 3.14, 1j,
_test_gen(),
diff --git a/Misc/NEWS b/Misc/NEWS
index 68b9907..558366f 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -69,6 +69,8 @@ Library
- Issue #28704: Fix create_unix_server to support Path-like objects
(PEP 519).
+- Issue #28720: Add collections.abc.AsyncGenerator.
+
Documentation
-------------