[Python-checkins] cpython (3.5): Issue 24315: Make collections.abc.Coroutine derived from Awaitable

yury.selivanov python-checkins at python.org
Fri May 29 15:01:56 CEST 2015


https://hg.python.org/cpython/rev/968af3838553
changeset:   96348:968af3838553
branch:      3.5
parent:      96344:3254a3c0acb4
user:        Yury Selivanov <yselivanov at sprymix.com>
date:        Fri May 29 09:01:29 2015 -0400
summary:
  Issue 24315: Make collections.abc.Coroutine derived from Awaitable

files:
  Doc/library/collections.abc.rst      |  10 +-
  Lib/_collections_abc.py              |  49 +++++++++------
  Lib/test/test_asyncio/test_pep492.py |  14 +--
  Lib/test/test_collections.py         |  34 ++++++++--
  4 files changed, 67 insertions(+), 40 deletions(-)


diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst
--- a/Doc/library/collections.abc.rst
+++ b/Doc/library/collections.abc.rst
@@ -82,7 +82,7 @@
                            :class:`Set`                                   ``__iter__``
 :class:`ValuesView`        :class:`MappingView`                           ``__contains__``, ``__iter__``
 :class:`Awaitable`                                ``__await__``
-:class:`Coroutine`                                ``send``, ``throw``     ``close``
+:class:`Coroutine`         :class:`Awaitable`     ``send``, ``throw``     ``close``
 :class:`AsyncIterable`                            ``__aiter__``
 :class:`AsyncIterator`     :class:`AsyncIterable` ``__anext__``           ``__aiter__``
 ========================== ====================== ======================= ====================================================
@@ -166,10 +166,10 @@
 
    ABC for coroutine compatible classes that implement a subset of
    generator methods defined in :pep:`342`, namely:
-   :meth:`~generator.send`, :meth:`~generator.throw` and
-   :meth:`~generator.close` methods.  All :class:`Coroutine` instances
-   are also instances of :class:`Awaitable`.  See also the definition
-   of :term:`coroutine`.
+   :meth:`~generator.send`, :meth:`~generator.throw`,
+   :meth:`~generator.close` methods.  :meth:`__await__` must also be
+   implemented.  All :class:`Coroutine` instances are also instances of
+   :class:`Awaitable`.  See also the definition of :term:`coroutine`.
 
    .. versionadded:: 3.5
 
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -75,7 +75,7 @@
         return NotImplemented
 
 
-class _CoroutineMeta(ABCMeta):
+class _AwaitableMeta(ABCMeta):
 
     def __instancecheck__(cls, instance):
         # 0x80 = CO_COROUTINE
@@ -92,7 +92,26 @@
         return super().__instancecheck__(instance)
 
 
-class Coroutine(metaclass=_CoroutineMeta):
+class Awaitable(metaclass=_AwaitableMeta):
+
+    __slots__ = ()
+
+    @abstractmethod
+    def __await__(self):
+        yield
+
+    @classmethod
+    def __subclasshook__(cls, C):
+        if cls is Awaitable:
+            for B in C.__mro__:
+                if "__await__" in B.__dict__:
+                    if B.__dict__["__await__"]:
+                        return True
+                    break
+        return NotImplemented
+
+
+class Coroutine(Awaitable):
 
     __slots__ = ()
 
@@ -126,27 +145,19 @@
         else:
             raise RuntimeError("coroutine ignored GeneratorExit")
 
-
-class Awaitable(metaclass=_CoroutineMeta):
-
-    __slots__ = ()
-
-    @abstractmethod
-    def __await__(self):
-        yield
-
     @classmethod
     def __subclasshook__(cls, C):
-        if cls is Awaitable:
-            for B in C.__mro__:
-                if "__await__" in B.__dict__:
-                    if B.__dict__["__await__"]:
-                        return True
-                    break
+        if cls is Coroutine:
+            mro = C.__mro__
+            for method in ('__await__', 'send', 'throw', 'close'):
+                for base in mro:
+                    if method in base.__dict__:
+                        break
+                else:
+                    return NotImplemented
+            return True
         return NotImplemented
 
-Awaitable.register(Coroutine)
-
 
 class AsyncIterable(metaclass=ABCMeta):
 
diff --git a/Lib/test/test_asyncio/test_pep492.py b/Lib/test/test_asyncio/test_pep492.py
--- a/Lib/test/test_asyncio/test_pep492.py
+++ b/Lib/test/test_asyncio/test_pep492.py
@@ -97,18 +97,14 @@
         finally:
             f.close() # silence warning
 
-        class FakeCoro(collections.abc.Coroutine):
+        # Test that asyncio.iscoroutine() uses collections.abc.Coroutine
+        class FakeCoro:
             def send(self, value): pass
             def throw(self, typ, val=None, tb=None): pass
+            def close(self): pass
+            def __await__(self): yield
 
-        fc = FakeCoro()
-        try:
-            self.assertTrue(asyncio.iscoroutine(fc))
-        finally:
-            # To make sure that ABCMeta caches are freed
-            # from FakeCoro ASAP.
-            fc = FakeCoro = None
-            support.gc_collect()
+        self.assertTrue(asyncio.iscoroutine(FakeCoro()))
 
 
 if __name__ == '__main__':
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -496,6 +496,8 @@
                 return value
             def throw(self, typ, val=None, tb=None):
                 super().throw(typ, val, tb)
+            def __await__(self):
+                yield
 
         non_samples = [None, int(), gen(), object()]
         for x in non_samples:
@@ -515,13 +517,7 @@
         self.assertIsInstance(c, Awaitable)
         c.close() # awoid RuntimeWarning that coro() was not awaited
 
-        class CoroLike:
-            def send(self, value):
-                pass
-            def throw(self, typ, val=None, tb=None):
-                pass
-            def close(self):
-                pass
+        class CoroLike: pass
         Coroutine.register(CoroLike)
         self.assertTrue(isinstance(CoroLike(), Awaitable))
         self.assertTrue(issubclass(CoroLike, Awaitable))
@@ -548,6 +544,8 @@
                 return value
             def throw(self, typ, val=None, tb=None):
                 super().throw(typ, val, tb)
+            def __await__(self):
+                yield
 
         non_samples = [None, int(), gen(), object(), Bar()]
         for x in non_samples:
@@ -567,6 +565,28 @@
         self.assertIsInstance(c, Coroutine)
         c.close() # awoid RuntimeWarning that coro() was not awaited
 
+        class CoroLike:
+            def send(self, value):
+                pass
+            def throw(self, typ, val=None, tb=None):
+                pass
+            def close(self):
+                pass
+            def __await__(self):
+                pass
+        self.assertTrue(isinstance(CoroLike(), Coroutine))
+        self.assertTrue(issubclass(CoroLike, Coroutine))
+
+        class CoroLike:
+            def send(self, value):
+                pass
+            def close(self):
+                pass
+            def __await__(self):
+                pass
+        self.assertFalse(isinstance(CoroLike(), Coroutine))
+        self.assertFalse(issubclass(CoroLike, Coroutine))
+
     def test_Hashable(self):
         # Check some non-hashables
         non_samples = [bytearray(), list(), set(), dict()]

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list