]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #28600: Optimize loop.call_soon().
authorYury Selivanov <yury@magic.io>
Thu, 3 Nov 2016 22:09:24 +0000 (15:09 -0700)
committerYury Selivanov <yury@magic.io>
Thu, 3 Nov 2016 22:09:24 +0000 (15:09 -0700)
Run expensive type checks only in debug mode.  In addition, stop
supporting passing handles to loop.run_in_executor.

Lib/asyncio/base_events.py
Lib/asyncio/events.py
Lib/test/test_asyncio/test_base_events.py
Lib/test/test_asyncio/test_events.py
Misc/NEWS

index b3e318e1b1cc6030620cf96829b1bff25882f353..ddf041d4d06d11ac427bb8f7ba9c2d714ff59e3c 100644 (file)
@@ -528,12 +528,10 @@ class BaseEventLoop(events.AbstractEventLoop):
 
         Absolute time corresponds to the event loop's time() method.
         """
-        if (coroutines.iscoroutine(callback)
-        or coroutines.iscoroutinefunction(callback)):
-            raise TypeError("coroutines cannot be used with call_at()")
         self._check_closed()
         if self._debug:
             self._check_thread()
+            self._check_callback(callback, 'call_at')
         timer = events.TimerHandle(when, callback, args, self)
         if timer._source_traceback:
             del timer._source_traceback[-1]
@@ -551,18 +549,27 @@ class BaseEventLoop(events.AbstractEventLoop):
         Any positional arguments after the callback will be passed to
         the callback when it is called.
         """
+        self._check_closed()
         if self._debug:
             self._check_thread()
+            self._check_callback(callback, 'call_soon')
         handle = self._call_soon(callback, args)
         if handle._source_traceback:
             del handle._source_traceback[-1]
         return handle
 
+    def _check_callback(self, callback, method):
+        if (coroutines.iscoroutine(callback) or
+                coroutines.iscoroutinefunction(callback)):
+            raise TypeError(
+                "coroutines cannot be used with {}()".format(method))
+        if not callable(callback):
+            raise TypeError(
+                'a callable object was expected by {}(), got {!r}'.format(
+                    method, callback))
+
+
     def _call_soon(self, callback, args):
-        if (coroutines.iscoroutine(callback)
-        or coroutines.iscoroutinefunction(callback)):
-            raise TypeError("coroutines cannot be used with call_soon()")
-        self._check_closed()
         handle = events.Handle(callback, args, self)
         if handle._source_traceback:
             del handle._source_traceback[-1]
@@ -588,6 +595,9 @@ class BaseEventLoop(events.AbstractEventLoop):
 
     def call_soon_threadsafe(self, callback, *args):
         """Like call_soon(), but thread-safe."""
+        self._check_closed()
+        if self._debug:
+            self._check_callback(callback, 'call_soon_threadsafe')
         handle = self._call_soon(callback, args)
         if handle._source_traceback:
             del handle._source_traceback[-1]
@@ -595,21 +605,9 @@ class BaseEventLoop(events.AbstractEventLoop):
         return handle
 
     def run_in_executor(self, executor, func, *args):
-        if (coroutines.iscoroutine(func)
-        or coroutines.iscoroutinefunction(func)):
-            raise TypeError("coroutines cannot be used with run_in_executor()")
         self._check_closed()
-        if isinstance(func, events.Handle):
-            assert not args
-            assert not isinstance(func, events.TimerHandle)
-            warnings.warn(
-                "Passing Handle to loop.run_in_executor() is deprecated",
-                DeprecationWarning)
-            if func._cancelled:
-                f = self.create_future()
-                f.set_result(None)
-                return f
-            func, args = func._callback, func._args
+        if self._debug:
+            self._check_callback(func, 'run_in_executor')
         if executor is None:
             executor = self._default_executor
             if executor is None:
index 3a33646df8f435d59bf5a469dcd90784fdabd6ce..b89b4b205a4437b07ed5ce0288d4ae03a02f6a71 100644 (file)
@@ -82,7 +82,6 @@ class Handle:
                  '_source_traceback', '_repr', '__weakref__')
 
     def __init__(self, callback, args, loop):
-        assert not isinstance(callback, Handle), 'A Handle is not a callback'
         self._loop = loop
         self._callback = callback
         self._args = args
index a9aba0ffb642a60aa6a34a40bf941b1ff6d3bf69..39131256a0a0b94e63806e3fc5775b67ace27f53 100644 (file)
@@ -235,6 +235,11 @@ class BaseEventLoopTests(test_utils.TestCase):
         self.assertIsInstance(h, asyncio.Handle)
         self.assertIn(h, self.loop._ready)
 
+    def test_call_soon_non_callable(self):
+        self.loop.set_debug(True)
+        with self.assertRaisesRegex(TypeError, 'a callable object'):
+            self.loop.call_soon(1)
+
     def test_call_later(self):
         def cb():
             pass
@@ -341,47 +346,21 @@ class BaseEventLoopTests(test_utils.TestCase):
         # check disabled if debug mode is disabled
         test_thread(self.loop, False, create_loop=True)
 
-    def test_run_once_in_executor_handle(self):
-        def cb():
-            pass
-
-        self.assertRaises(
-            AssertionError, self.loop.run_in_executor,
-            None, asyncio.Handle(cb, (), self.loop), ('',))
-        self.assertRaises(
-            AssertionError, self.loop.run_in_executor,
-            None, asyncio.TimerHandle(10, cb, (), self.loop))
-
-    def test_run_once_in_executor_cancelled(self):
-        def cb():
-            pass
-        h = asyncio.Handle(cb, (), self.loop)
-        h.cancel()
-
-        with self.assertWarnsRegex(DeprecationWarning, "Passing Handle"):
-            f = self.loop.run_in_executor(None, h)
-        self.assertIsInstance(f, asyncio.Future)
-        self.assertTrue(f.done())
-        self.assertIsNone(f.result())
-
     def test_run_once_in_executor_plain(self):
         def cb():
             pass
-        h = asyncio.Handle(cb, (), self.loop)
         f = asyncio.Future(loop=self.loop)
         executor = mock.Mock()
         executor.submit.return_value = f
 
         self.loop.set_default_executor(executor)
 
-        with self.assertWarnsRegex(DeprecationWarning, "Passing Handle"):
-            res = self.loop.run_in_executor(None, h)
+        res = self.loop.run_in_executor(None, cb)
         self.assertIs(f, res)
 
         executor = mock.Mock()
         executor.submit.return_value = f
-        with self.assertWarnsRegex(DeprecationWarning, "Passing Handle"):
-            res = self.loop.run_in_executor(executor, h)
+        res = self.loop.run_in_executor(executor, cb)
         self.assertIs(f, res)
         self.assertTrue(executor.submit.called)
 
@@ -1666,6 +1645,7 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase):
         def simple_coroutine():
             pass
 
+        self.loop.set_debug(True)
         coro_func = simple_coroutine
         coro_obj = coro_func()
         self.addCleanup(coro_obj.close)
index 7df926f191dd29d36d08ed51db55c158c8ee0aca..d8946e38f229b28a78cfebbb9f32cdd666b1a04b 100644 (file)
@@ -2249,13 +2249,6 @@ class HandleTests(test_utils.TestCase):
         h.cancel()
         self.assertTrue(h._cancelled)
 
-    def test_handle_from_handle(self):
-        def callback(*args):
-            return args
-        h1 = asyncio.Handle(callback, (), loop=self.loop)
-        self.assertRaises(
-            AssertionError, asyncio.Handle, h1, (), self.loop)
-
     def test_callback_with_exception(self):
         def callback():
             raise ValueError()
index 5410c77c6299ff565b3aa0cd9af1541c1ca17cd4..5c936bd483eaca2eeb574978fec36b38a9c4e993 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -441,6 +441,8 @@ Library
   threadpool executor.
   Initial patch by Hans Lawrenz.
 
+- Issue #28600: Optimize loop.call_soon().
+
 IDLE
 ----