summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYury Selivanov <yselivanov@sprymix.com>2015-05-29 13:01:29 (GMT)
committerYury Selivanov <yselivanov@sprymix.com>2015-05-29 13:01:29 (GMT)
commit56fc61402533dc550244efe3e860242872f35bad (patch)
tree859b4c58dac06f2760d97814f36f8664a50af121
parent8fa6d4f75311bb459da50226c726d1ab7ccf115b (diff)
downloadcpython-56fc61402533dc550244efe3e860242872f35bad.zip
cpython-56fc61402533dc550244efe3e860242872f35bad.tar.gz
cpython-56fc61402533dc550244efe3e860242872f35bad.tar.bz2
Issue 24315: Make collections.abc.Coroutine derived from Awaitable
-rw-r--r--Doc/library/collections.abc.rst10
-rw-r--r--Lib/_collections_abc.py49
-rw-r--r--Lib/test/test_asyncio/test_pep492.py14
-rw-r--r--Lib/test/test_collections.py34
4 files changed, 67 insertions, 40 deletions
diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst
index 0039bb2..8c710ef 100644
--- a/Doc/library/collections.abc.rst
+++ b/Doc/library/collections.abc.rst
@@ -82,7 +82,7 @@ ABC Inherits from Abstract Methods Mixin
: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 Inherits from Abstract Methods Mixin
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
index 0ca7deb..a02b219 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -75,7 +75,7 @@ class Hashable(metaclass=ABCMeta):
return NotImplemented
-class _CoroutineMeta(ABCMeta):
+class _AwaitableMeta(ABCMeta):
def __instancecheck__(cls, instance):
# 0x80 = CO_COROUTINE
@@ -92,7 +92,26 @@ class _CoroutineMeta(ABCMeta):
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 @@ class Coroutine(metaclass=_CoroutineMeta):
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
index e25e257..8fc6cba 100644
--- a/Lib/test/test_asyncio/test_pep492.py
+++ b/Lib/test/test_asyncio/test_pep492.py
@@ -97,18 +97,14 @@ class CoroutineTests(BaseTest):
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
index ec86466..dc79b69 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -496,6 +496,8 @@ class TestOneTrickPonyABCs(ABCTestCase):
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 @@ class TestOneTrickPonyABCs(ABCTestCase):
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 @@ class TestOneTrickPonyABCs(ABCTestCase):
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 @@ class TestOneTrickPonyABCs(ABCTestCase):
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()]