Commit 491a9126 authored by Yury Selivanov's avatar Yury Selivanov

Issue #28600: Optimize loop.call_soon().

Run expensive type checks only in debug mode.  In addition, stop
supporting passing handles to loop.run_in_executor.
parent 9e80eeb2
...@@ -528,12 +528,10 @@ class BaseEventLoop(events.AbstractEventLoop): ...@@ -528,12 +528,10 @@ class BaseEventLoop(events.AbstractEventLoop):
Absolute time corresponds to the event loop's time() method. 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() self._check_closed()
if self._debug: if self._debug:
self._check_thread() self._check_thread()
self._check_callback(callback, 'call_at')
timer = events.TimerHandle(when, callback, args, self) timer = events.TimerHandle(when, callback, args, self)
if timer._source_traceback: if timer._source_traceback:
del timer._source_traceback[-1] del timer._source_traceback[-1]
...@@ -551,18 +549,27 @@ class BaseEventLoop(events.AbstractEventLoop): ...@@ -551,18 +549,27 @@ class BaseEventLoop(events.AbstractEventLoop):
Any positional arguments after the callback will be passed to Any positional arguments after the callback will be passed to
the callback when it is called. the callback when it is called.
""" """
self._check_closed()
if self._debug: if self._debug:
self._check_thread() self._check_thread()
self._check_callback(callback, 'call_soon')
handle = self._call_soon(callback, args) handle = self._call_soon(callback, args)
if handle._source_traceback: if handle._source_traceback:
del handle._source_traceback[-1] del handle._source_traceback[-1]
return handle 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): 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) handle = events.Handle(callback, args, self)
if handle._source_traceback: if handle._source_traceback:
del handle._source_traceback[-1] del handle._source_traceback[-1]
...@@ -588,6 +595,9 @@ class BaseEventLoop(events.AbstractEventLoop): ...@@ -588,6 +595,9 @@ class BaseEventLoop(events.AbstractEventLoop):
def call_soon_threadsafe(self, callback, *args): def call_soon_threadsafe(self, callback, *args):
"""Like call_soon(), but thread-safe.""" """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) handle = self._call_soon(callback, args)
if handle._source_traceback: if handle._source_traceback:
del handle._source_traceback[-1] del handle._source_traceback[-1]
...@@ -595,21 +605,9 @@ class BaseEventLoop(events.AbstractEventLoop): ...@@ -595,21 +605,9 @@ class BaseEventLoop(events.AbstractEventLoop):
return handle return handle
def run_in_executor(self, executor, func, *args): 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() self._check_closed()
if isinstance(func, events.Handle): if self._debug:
assert not args self._check_callback(func, 'run_in_executor')
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 executor is None: if executor is None:
executor = self._default_executor executor = self._default_executor
if executor is None: if executor is None:
......
...@@ -82,7 +82,6 @@ class Handle: ...@@ -82,7 +82,6 @@ class Handle:
'_source_traceback', '_repr', '__weakref__') '_source_traceback', '_repr', '__weakref__')
def __init__(self, callback, args, loop): def __init__(self, callback, args, loop):
assert not isinstance(callback, Handle), 'A Handle is not a callback'
self._loop = loop self._loop = loop
self._callback = callback self._callback = callback
self._args = args self._args = args
......
...@@ -235,6 +235,11 @@ class BaseEventLoopTests(test_utils.TestCase): ...@@ -235,6 +235,11 @@ class BaseEventLoopTests(test_utils.TestCase):
self.assertIsInstance(h, asyncio.Handle) self.assertIsInstance(h, asyncio.Handle)
self.assertIn(h, self.loop._ready) 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 test_call_later(self):
def cb(): def cb():
pass pass
...@@ -341,47 +346,21 @@ class BaseEventLoopTests(test_utils.TestCase): ...@@ -341,47 +346,21 @@ class BaseEventLoopTests(test_utils.TestCase):
# check disabled if debug mode is disabled # check disabled if debug mode is disabled
test_thread(self.loop, False, create_loop=True) 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 test_run_once_in_executor_plain(self):
def cb(): def cb():
pass pass
h = asyncio.Handle(cb, (), self.loop)
f = asyncio.Future(loop=self.loop) f = asyncio.Future(loop=self.loop)
executor = mock.Mock() executor = mock.Mock()
executor.submit.return_value = f executor.submit.return_value = f
self.loop.set_default_executor(executor) self.loop.set_default_executor(executor)
with self.assertWarnsRegex(DeprecationWarning, "Passing Handle"): res = self.loop.run_in_executor(None, cb)
res = self.loop.run_in_executor(None, h)
self.assertIs(f, res) self.assertIs(f, res)
executor = mock.Mock() executor = mock.Mock()
executor.submit.return_value = f executor.submit.return_value = f
with self.assertWarnsRegex(DeprecationWarning, "Passing Handle"): res = self.loop.run_in_executor(executor, cb)
res = self.loop.run_in_executor(executor, h)
self.assertIs(f, res) self.assertIs(f, res)
self.assertTrue(executor.submit.called) self.assertTrue(executor.submit.called)
...@@ -1666,6 +1645,7 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase): ...@@ -1666,6 +1645,7 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase):
def simple_coroutine(): def simple_coroutine():
pass pass
self.loop.set_debug(True)
coro_func = simple_coroutine coro_func = simple_coroutine
coro_obj = coro_func() coro_obj = coro_func()
self.addCleanup(coro_obj.close) self.addCleanup(coro_obj.close)
......
...@@ -2249,13 +2249,6 @@ class HandleTests(test_utils.TestCase): ...@@ -2249,13 +2249,6 @@ class HandleTests(test_utils.TestCase):
h.cancel() h.cancel()
self.assertTrue(h._cancelled) 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 test_callback_with_exception(self):
def callback(): def callback():
raise ValueError() raise ValueError()
......
...@@ -441,6 +441,8 @@ Library ...@@ -441,6 +441,8 @@ Library
threadpool executor. threadpool executor.
Initial patch by Hans Lawrenz. Initial patch by Hans Lawrenz.
- Issue #28600: Optimize loop.call_soon().
IDLE IDLE
---- ----
......
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