summaryrefslogtreecommitdiff
path: root/Lib
diff options
context:
space:
mode:
authorYury Selivanov <yury@magic.io>2017-12-19 07:18:45 -0500
committerGitHub <noreply@github.com>2017-12-19 07:18:45 -0500
commita9d7e552c72b6e9515e76a1dd4b247da86da23de (patch)
treee2f05acc41f3eb90bbd29377957867adff5f79c9 /Lib
parenta7bd64c0c01379e9b82e86ad41a301329a0775d9 (diff)
downloadcpython-git-a9d7e552c72b6e9515e76a1dd4b247da86da23de.tar.gz
bpo-32357: Optimize asyncio.iscoroutine() for non-native coroutines (#4915)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/asyncio/coroutines.py21
-rw-r--r--Lib/test/test_asyncio/test_tasks.py43
2 files changed, 59 insertions, 5 deletions
diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py
index e3c0162dd1..9c860a452b 100644
--- a/Lib/asyncio/coroutines.py
+++ b/Lib/asyncio/coroutines.py
@@ -1,5 +1,6 @@
__all__ = 'coroutine', 'iscoroutinefunction', 'iscoroutine'
+import collections.abc
import functools
import inspect
import os
@@ -7,8 +8,6 @@ import sys
import traceback
import types
-from collections.abc import Awaitable, Coroutine
-
from . import base_futures
from . import constants
from . import format_helpers
@@ -162,7 +161,7 @@ def coroutine(func):
except AttributeError:
pass
else:
- if isinstance(res, Awaitable):
+ if isinstance(res, collections.abc.Awaitable):
res = yield from await_meth()
return res
@@ -199,12 +198,24 @@ def iscoroutinefunction(func):
# Prioritize native coroutine check to speed-up
# asyncio.iscoroutine.
_COROUTINE_TYPES = (types.CoroutineType, types.GeneratorType,
- Coroutine, CoroWrapper)
+ collections.abc.Coroutine, CoroWrapper)
+_iscoroutine_typecache = set()
def iscoroutine(obj):
"""Return True if obj is a coroutine object."""
- return isinstance(obj, _COROUTINE_TYPES)
+ if type(obj) in _iscoroutine_typecache:
+ return True
+
+ if isinstance(obj, _COROUTINE_TYPES):
+ # Just in case we don't want to cache more than 100
+ # positive types. That shouldn't ever happen, unless
+ # someone stressing the system on purpose.
+ if len(_iscoroutine_typecache) < 100:
+ _iscoroutine_typecache.add(type(obj))
+ return True
+ else:
+ return False
def _format_coroutine(coro):
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index 4720661399..f1dbb99d4f 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -62,6 +62,20 @@ class Dummy:
pass
+class CoroLikeObject:
+ def send(self, v):
+ raise StopIteration(42)
+
+ def throw(self, *exc):
+ pass
+
+ def close(self):
+ pass
+
+ def __await__(self):
+ return self
+
+
class BaseTaskTests:
Task = None
@@ -2085,6 +2099,12 @@ class BaseTaskTests:
"a coroutine was expected, got 123"):
self.new_task(self.loop, 123)
+ # test it for the second time to ensure that caching
+ # in asyncio.iscoroutine() doesn't break things.
+ with self.assertRaisesRegex(TypeError,
+ "a coroutine was expected, got 123"):
+ self.new_task(self.loop, 123)
+
def test_create_task_with_oldstyle_coroutine(self):
@asyncio.coroutine
@@ -2095,6 +2115,12 @@ class BaseTaskTests:
self.assertIsInstance(task, self.Task)
self.loop.run_until_complete(task)
+ # test it for the second time to ensure that caching
+ # in asyncio.iscoroutine() doesn't break things.
+ task = self.new_task(self.loop, coro())
+ self.assertIsInstance(task, self.Task)
+ self.loop.run_until_complete(task)
+
def test_create_task_with_async_function(self):
async def coro():
@@ -2104,6 +2130,23 @@ class BaseTaskTests:
self.assertIsInstance(task, self.Task)
self.loop.run_until_complete(task)
+ # test it for the second time to ensure that caching
+ # in asyncio.iscoroutine() doesn't break things.
+ task = self.new_task(self.loop, coro())
+ self.assertIsInstance(task, self.Task)
+ self.loop.run_until_complete(task)
+
+ def test_create_task_with_asynclike_function(self):
+ task = self.new_task(self.loop, CoroLikeObject())
+ self.assertIsInstance(task, self.Task)
+ self.assertEqual(self.loop.run_until_complete(task), 42)
+
+ # test it for the second time to ensure that caching
+ # in asyncio.iscoroutine() doesn't break things.
+ task = self.new_task(self.loop, CoroLikeObject())
+ self.assertIsInstance(task, self.Task)
+ self.assertEqual(self.loop.run_until_complete(task), 42)
+
def test_bare_create_task(self):
async def inner():