mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Merge 3.6 (issue #28720)
This commit is contained in:
commit
6592dbd2a8
4 changed files with 153 additions and 2 deletions
|
@ -92,6 +92,7 @@ ABC Inherits from Abstract Methods Mixin
|
||||||
:class:`Coroutine` :class:`Awaitable` ``send``, ``throw`` ``close``
|
:class:`Coroutine` :class:`Awaitable` ``send``, ``throw`` ``close``
|
||||||
:class:`AsyncIterable` ``__aiter__``
|
:class:`AsyncIterable` ``__aiter__``
|
||||||
:class:`AsyncIterator` :class:`AsyncIterable` ``__anext__`` ``__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
|
.. 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
|
These ABCs allow us to ask classes or instances if they provide
|
||||||
particular functionality, for example::
|
particular functionality, for example::
|
||||||
|
|
|
@ -912,6 +912,10 @@ The new :class:`~collections.abc.Reversible` abstract base class represents
|
||||||
iterable classes that also provide the :meth:`__reversed__`.
|
iterable classes that also provide the :meth:`__reversed__`.
|
||||||
(Contributed by Ivan Levkivskyi in :issue:`25987`.)
|
(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
|
The :func:`~collections.namedtuple` function now accepts an optional
|
||||||
keyword argument *module*, which, when specified, is used for
|
keyword argument *module*, which, when specified, is used for
|
||||||
the ``__module__`` attribute of the returned named tuple class.
|
the ``__module__`` attribute of the returned named tuple class.
|
||||||
|
|
|
@ -9,7 +9,8 @@ Unit tests are in test_collections.
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
|
__all__ = ["Awaitable", "Coroutine",
|
||||||
|
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
|
||||||
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
|
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
|
||||||
"Sized", "Container", "Callable", "Collection",
|
"Sized", "Container", "Callable", "Collection",
|
||||||
"Set", "MutableSet",
|
"Set", "MutableSet",
|
||||||
|
@ -59,6 +60,11 @@ _coro = _coro()
|
||||||
coroutine = type(_coro)
|
coroutine = type(_coro)
|
||||||
_coro.close() # Prevent ResourceWarning
|
_coro.close() # Prevent ResourceWarning
|
||||||
del _coro
|
del _coro
|
||||||
|
## asynchronous generator ##
|
||||||
|
async def _ag(): yield
|
||||||
|
_ag = _ag()
|
||||||
|
async_generator = type(_ag)
|
||||||
|
del _ag
|
||||||
|
|
||||||
|
|
||||||
### ONE-TRICK PONIES ###
|
### ONE-TRICK PONIES ###
|
||||||
|
@ -183,6 +189,57 @@ class AsyncIterator(AsyncIterable):
|
||||||
return NotImplemented
|
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):
|
class Iterable(metaclass=ABCMeta):
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
|
@ -19,7 +19,8 @@ from collections import namedtuple, Counter, OrderedDict, _count_elements
|
||||||
from collections import UserDict, UserString, UserList
|
from collections import UserDict, UserString, UserList
|
||||||
from collections import ChainMap
|
from collections import ChainMap
|
||||||
from collections import deque
|
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 Hashable, Iterable, Iterator, Generator, Reversible
|
||||||
from collections.abc import Sized, Container, Callable, Collection
|
from collections.abc import Sized, Container, Callable, Collection
|
||||||
from collections.abc import Set, MutableSet
|
from collections.abc import Set, MutableSet
|
||||||
|
@ -959,6 +960,87 @@ class TestOneTrickPonyABCs(ABCTestCase):
|
||||||
|
|
||||||
self.assertRaises(RuntimeError, IgnoreGeneratorExit().close)
|
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):
|
def test_Sized(self):
|
||||||
non_samples = [None, 42, 3.14, 1j,
|
non_samples = [None, 42, 3.14, 1j,
|
||||||
_test_gen(),
|
_test_gen(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue