]> granicus.if.org Git - python/commitdiff
Issue 24184: Add AsyncIterator and AsyncIterable to collections.abc.
authorYury Selivanov <yselivanov@sprymix.com>
Thu, 14 May 2015 16:19:16 +0000 (12:19 -0400)
committerYury Selivanov <yselivanov@sprymix.com>
Thu, 14 May 2015 16:19:16 +0000 (12:19 -0400)
Lib/_collections_abc.py
Lib/test/test_collections.py
Misc/NEWS

index ea804031d5ee9117bd338cd3b9b707afa696854f..2f8354312498a95f2ce58344860434e41315a3e2 100644 (file)
@@ -9,7 +9,7 @@ Unit tests are in test_collections.
 from abc import ABCMeta, abstractmethod
 import sys
 
-__all__ = ["Awaitable", "Coroutine",
+__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
            "Hashable", "Iterable", "Iterator", "Generator",
            "Sized", "Container", "Callable",
            "Set", "MutableSet",
@@ -148,6 +148,43 @@ class Awaitable(metaclass=_CoroutineMeta):
 Awaitable.register(Coroutine)
 
 
+class AsyncIterable(metaclass=ABCMeta):
+
+    __slots__ = ()
+
+    @abstractmethod
+    async def __aiter__(self):
+        return AsyncIterator()
+
+    @classmethod
+    def __subclasshook__(cls, C):
+        if cls is AsyncIterable:
+            if any("__aiter__" in B.__dict__ for B in C.__mro__):
+                return True
+        return NotImplemented
+
+
+class AsyncIterator(AsyncIterable):
+
+    __slots__ = ()
+
+    @abstractmethod
+    async def __anext__(self):
+        """Return the next item or raise StopAsyncIteration when exhausted."""
+        raise StopAsyncIteration
+
+    async def __aiter__(self):
+        return self
+
+    @classmethod
+    def __subclasshook__(cls, C):
+        if cls is AsyncIterator:
+            if (any("__anext__" in B.__dict__ for B in C.__mro__) and
+                any("__aiter__" in B.__dict__ for B in C.__mro__)):
+                return True
+        return NotImplemented
+
+
 class Iterable(metaclass=ABCMeta):
 
     __slots__ = ()
index 16c26ad1f4abe19e7d62bd5d3a725601a3db98ca..d96eae7458945f052d2b0c62963fe489cf397dd2 100644 (file)
@@ -15,7 +15,7 @@ import types
 from collections import UserDict
 from collections import ChainMap
 from collections import deque
-from collections.abc import Awaitable, Coroutine
+from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable
 from collections.abc import Hashable, Iterable, Iterator, Generator
 from collections.abc import Sized, Container, Callable
 from collections.abc import Set, MutableSet
@@ -573,6 +573,40 @@ class TestOneTrickPonyABCs(ABCTestCase):
         self.validate_abstract_methods(Hashable, '__hash__')
         self.validate_isinstance(Hashable, '__hash__')
 
+    def test_AsyncIterable(self):
+        class AI:
+            async def __aiter__(self):
+                return self
+        self.assertTrue(isinstance(AI(), AsyncIterable))
+        self.assertTrue(issubclass(AI, AsyncIterable))
+        # Check some non-iterables
+        non_samples = [None, object, []]
+        for x in non_samples:
+            self.assertNotIsInstance(x, AsyncIterable)
+            self.assertFalse(issubclass(type(x), AsyncIterable), repr(type(x)))
+        self.validate_abstract_methods(AsyncIterable, '__aiter__')
+        self.validate_isinstance(AsyncIterable, '__aiter__')
+
+    def test_AsyncIterator(self):
+        class AI:
+            async def __aiter__(self):
+                return self
+            async def __anext__(self):
+                raise StopAsyncIteration
+        self.assertTrue(isinstance(AI(), AsyncIterator))
+        self.assertTrue(issubclass(AI, AsyncIterator))
+        non_samples = [None, object, []]
+        # Check some non-iterables
+        for x in non_samples:
+            self.assertNotIsInstance(x, AsyncIterator)
+            self.assertFalse(issubclass(type(x), AsyncIterator), repr(type(x)))
+        # Similarly to regular iterators (see issue 10565)
+        class AnextOnly:
+            async def __anext__(self):
+                raise StopAsyncIteration
+        self.assertNotIsInstance(AnextOnly(), AsyncIterator)
+        self.validate_abstract_methods(AsyncIterator, '__anext__', '__aiter__')
+
     def test_Iterable(self):
         # Check some non-iterables
         non_samples = [None, 42, 3.14, 1j]
index b4338489e59e4faf4ff7870b964f411a85c90d55..b181f7682320f6969f4c2b2696a7e7331b563949 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -122,6 +122,9 @@ Library
 - Issue 24179: Support 'async for' for asyncio.StreamReader.
   Contributed by Yury Selivanov.
 
+- Issue 24184: Add AsyncIterator and AsyncIterable ABCs to
+  collections.abc.  Contributed by Yury Selivanov.
+
 Tests
 -----