diff options
author | Yury Selivanov <yselivanov@sprymix.com> | 2015-05-29 13:01:29 (GMT) |
---|---|---|
committer | Yury Selivanov <yselivanov@sprymix.com> | 2015-05-29 13:01:29 (GMT) |
commit | 56fc61402533dc550244efe3e860242872f35bad (patch) | |
tree | 859b4c58dac06f2760d97814f36f8664a50af121 | |
parent | 8fa6d4f75311bb459da50226c726d1ab7ccf115b (diff) | |
download | cpython-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.rst | 10 | ||||
-rw-r--r-- | Lib/_collections_abc.py | 49 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_pep492.py | 14 | ||||
-rw-r--r-- | 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 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()] |