diff -r bc1a178b3bc8 Doc/library/collections.abc.rst --- a/Doc/library/collections.abc.rst Sat Apr 18 05:54:02 2015 +0200 +++ b/Doc/library/collections.abc.rst Mon Apr 20 21:56:35 2015 +0200 @@ -40,6 +40,7 @@ :class:`Hashable` ``__hash__`` :class:`Iterable` ``__iter__`` :class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__`` +:class:`Generator` :class:`Generator` ``send`` ``throw``, ``close``, ``__next__`` :class:`Sized` ``__len__`` :class:`Callable` ``__call__`` @@ -102,6 +103,13 @@ :meth:`~iterator.__next__` methods. See also the definition of :term:`iterator`. +.. class:: Generator + + ABC for generator classes that implement the protocol defined in + :pep:`342` that extends iterators with the :meth:`send()`, + :meth:`throw()` and :meth:`close` methods. See also the definition of + :term:`generator`. + .. class:: Sequence MutableSequence diff -r bc1a178b3bc8 Lib/_collections_abc.py --- a/Lib/_collections_abc.py Sat Apr 18 05:54:02 2015 +0200 +++ b/Lib/_collections_abc.py Mon Apr 20 21:56:35 2015 +0200 @@ -9,7 +9,7 @@ from abc import ABCMeta, abstractmethod import sys -__all__ = ["Hashable", "Iterable", "Iterator", +__all__ = ["Hashable", "Iterable", "Iterator", "Generator", "Sized", "Container", "Callable", "Set", "MutableSet", "Mapping", "MutableMapping", @@ -50,6 +50,7 @@ dict_items = type({}.items()) ## misc ## mappingproxy = type(type.__dict__) +generator = type((lambda: (yield))()) ### ONE-TRICK PONIES ### @@ -124,6 +125,46 @@ Iterator.register(tuple_iterator) Iterator.register(zip_iterator) + +class Generator(Iterator): + + __slots__ = () + + def __next__(self): + """Return the next item from the generator. + When exhausted, raise StopIteration. + """ + return self.send(None) + + @abstractmethod + def send(self, value): + """Send a value into the generator. + Return next yielded value or raise StopIteration. + """ + raise StopIteration + + def throw(self, typ, val=None, rb=None): + """Raise an exception in the generator. + Return next yielded value or raise StopIteration. + """ + raise StopIteration + + def close(self): + """Raise GeneratorExit inside generator. + """ + + @classmethod + def __subclasshook__(cls, C): + if cls is Generator: + if (any("send" in B.__dict__ for B in C.__mro__) and + any("send" in B.__dict__ for B in C.__mro__)): + return True + return NotImplemented + + +Generator.register(generator) + + class Sized(metaclass=ABCMeta): __slots__ = () diff -r bc1a178b3bc8 Lib/test/test_collections.py --- a/Lib/test/test_collections.py Sat Apr 18 05:54:02 2015 +0200 +++ b/Lib/test/test_collections.py Mon Apr 20 21:56:35 2015 +0200 @@ -14,7 +14,7 @@ from collections import UserDict from collections import ChainMap from collections import deque -from collections.abc import Hashable, Iterable, Iterator +from collections.abc import Hashable, Iterable, Iterator, Generator from collections.abc import Sized, Container, Callable from collections.abc import Set, MutableSet from collections.abc import Mapping, MutableMapping, KeysView, ItemsView @@ -522,6 +522,22 @@ return self.assertNotIsInstance(NextOnly(), Iterator) + def test_Generator(self): + non_samples = [ + None, 42, 3.14, 1j, b"", "", (), [], {}, set(), + iter(()), iter([])] + for x in non_samples: + self.assertNotIsInstance(x, Generator) + self.assertFalse(issubclass(type(x), Generator), repr(type(x))) + + def gen(): + yield 1 + samples = [gen(), (lambda: (yield))()] + for x in samples: + self.assertIsInstance(x, Generator) + self.assertTrue(issubclass(type(x), Generator), repr(type(x))) + self.validate_abstract_methods(Generator, 'send') + def test_Sized(self): non_samples = [None, 42, 3.14, 1j, (lambda: (yield))(),