Commit a24fcfdf authored by Yury Selivanov's avatar Yury Selivanov

Issue 24315: Make collections.abc.Coroutine derived from Awaitable

(Merge 3.5)
parents 41a6a625 56fc6140
...@@ -82,7 +82,7 @@ ABC Inherits from Abstract Methods Mixin ...@@ -82,7 +82,7 @@ ABC Inherits from Abstract Methods Mixin
:class:`Set` ``__iter__`` :class:`Set` ``__iter__``
:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` :class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__``
:class:`Awaitable` ``__await__`` :class:`Awaitable` ``__await__``
:class:`Coroutine` ``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__``
========================== ====================== ======================= ==================================================== ========================== ====================== ======================= ====================================================
...@@ -166,10 +166,10 @@ ABC Inherits from Abstract Methods Mixin ...@@ -166,10 +166,10 @@ ABC Inherits from Abstract Methods Mixin
ABC for coroutine compatible classes that implement a subset of ABC for coroutine compatible classes that implement a subset of
generator methods defined in :pep:`342`, namely: generator methods defined in :pep:`342`, namely:
:meth:`~generator.send`, :meth:`~generator.throw` and :meth:`~generator.send`, :meth:`~generator.throw`,
:meth:`~generator.close` methods. All :class:`Coroutine` instances :meth:`~generator.close` methods. :meth:`__await__` must also be
are also instances of :class:`Awaitable`. See also the definition implemented. All :class:`Coroutine` instances are also instances of
of :term:`coroutine`. :class:`Awaitable`. See also the definition of :term:`coroutine`.
.. versionadded:: 3.5 .. versionadded:: 3.5
......
...@@ -75,7 +75,7 @@ class Hashable(metaclass=ABCMeta): ...@@ -75,7 +75,7 @@ class Hashable(metaclass=ABCMeta):
return NotImplemented return NotImplemented
class _CoroutineMeta(ABCMeta): class _AwaitableMeta(ABCMeta):
def __instancecheck__(cls, instance): def __instancecheck__(cls, instance):
# 0x80 = CO_COROUTINE # 0x80 = CO_COROUTINE
...@@ -92,7 +92,26 @@ class _CoroutineMeta(ABCMeta): ...@@ -92,7 +92,26 @@ class _CoroutineMeta(ABCMeta):
return super().__instancecheck__(instance) 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__ = () __slots__ = ()
...@@ -126,26 +145,18 @@ class Coroutine(metaclass=_CoroutineMeta): ...@@ -126,26 +145,18 @@ class Coroutine(metaclass=_CoroutineMeta):
else: else:
raise RuntimeError("coroutine ignored GeneratorExit") raise RuntimeError("coroutine ignored GeneratorExit")
class Awaitable(metaclass=_CoroutineMeta):
__slots__ = ()
@abstractmethod
def __await__(self):
yield
@classmethod @classmethod
def __subclasshook__(cls, C): def __subclasshook__(cls, C):
if cls is Awaitable: if cls is Coroutine:
for B in C.__mro__: mro = C.__mro__
if "__await__" in B.__dict__: for method in ('__await__', 'send', 'throw', 'close'):
if B.__dict__["__await__"]: for base in mro:
return True if method in base.__dict__:
break break
else:
return NotImplemented
return True
return NotImplemented return NotImplemented
Awaitable.register(Coroutine)
class AsyncIterable(metaclass=ABCMeta): class AsyncIterable(metaclass=ABCMeta):
......
...@@ -97,18 +97,14 @@ class CoroutineTests(BaseTest): ...@@ -97,18 +97,14 @@ class CoroutineTests(BaseTest):
finally: finally:
f.close() # silence warning 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 send(self, value): pass
def throw(self, typ, val=None, tb=None): pass def throw(self, typ, val=None, tb=None): pass
def close(self): pass
def __await__(self): yield
fc = FakeCoro() self.assertTrue(asyncio.iscoroutine(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()
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -496,6 +496,8 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -496,6 +496,8 @@ class TestOneTrickPonyABCs(ABCTestCase):
return value return value
def throw(self, typ, val=None, tb=None): def throw(self, typ, val=None, tb=None):
super().throw(typ, val, tb) super().throw(typ, val, tb)
def __await__(self):
yield
non_samples = [None, int(), gen(), object()] non_samples = [None, int(), gen(), object()]
for x in non_samples: for x in non_samples:
...@@ -515,13 +517,7 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -515,13 +517,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.assertIsInstance(c, Awaitable) self.assertIsInstance(c, Awaitable)
c.close() # awoid RuntimeWarning that coro() was not awaited c.close() # awoid RuntimeWarning that coro() was not awaited
class CoroLike: class CoroLike: pass
def send(self, value):
pass
def throw(self, typ, val=None, tb=None):
pass
def close(self):
pass
Coroutine.register(CoroLike) Coroutine.register(CoroLike)
self.assertTrue(isinstance(CoroLike(), Awaitable)) self.assertTrue(isinstance(CoroLike(), Awaitable))
self.assertTrue(issubclass(CoroLike, Awaitable)) self.assertTrue(issubclass(CoroLike, Awaitable))
...@@ -548,6 +544,8 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -548,6 +544,8 @@ class TestOneTrickPonyABCs(ABCTestCase):
return value return value
def throw(self, typ, val=None, tb=None): def throw(self, typ, val=None, tb=None):
super().throw(typ, val, tb) super().throw(typ, val, tb)
def __await__(self):
yield
non_samples = [None, int(), gen(), object(), Bar()] non_samples = [None, int(), gen(), object(), Bar()]
for x in non_samples: for x in non_samples:
...@@ -567,6 +565,28 @@ class TestOneTrickPonyABCs(ABCTestCase): ...@@ -567,6 +565,28 @@ class TestOneTrickPonyABCs(ABCTestCase):
self.assertIsInstance(c, Coroutine) self.assertIsInstance(c, Coroutine)
c.close() # awoid RuntimeWarning that coro() was not awaited 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): def test_Hashable(self):
# Check some non-hashables # Check some non-hashables
non_samples = [bytearray(), list(), set(), dict()] non_samples = [bytearray(), list(), set(), dict()]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment