diff --git a/socketio/asyncio_manager.py b/socketio/asyncio_manager.py index 581caa8..5322aa9 100644 --- a/socketio/asyncio_manager.py +++ b/socketio/asyncio_manager.py @@ -23,8 +23,9 @@ class AsyncManager(BaseManager): id = None tasks.append(self.server._emit_internal(sid, event, data, namespace, id)) - if tasks != []: - await asyncio.wait(tasks) + if tasks == []: # pragma: no cover + return + await asyncio.wait(tasks) async def close_room(self, room, namespace): """Remove all participants from a room. diff --git a/socketio/asyncio_server.py b/socketio/asyncio_server.py index ea0699a..38aade1 100644 --- a/socketio/asyncio_server.py +++ b/socketio/asyncio_server.py @@ -24,6 +24,9 @@ class AsyncServer(server.Server): packets. Custom json modules must have ``dumps`` and ``loads`` functions that are compatible with the standard library versions. + :param async_handlers: If set to ``True``, event handlers are executed in + separate threads. To run handlers synchronously, + set to ``False``. The default is ``False``. :param kwargs: Connection parameters for the underlying Engine.IO server. The Engine.IO configuration supports the following settings: @@ -55,11 +58,13 @@ class AsyncServer(server.Server): a logger object to use. To disable logging set to ``False``. """ - def __init__(self, client_manager=None, logger=False, json=None, **kwargs): + def __init__(self, client_manager=None, logger=False, json=None, + async_handlers=False, **kwargs): if client_manager is None: client_manager = asyncio_manager.AsyncManager() super().__init__(client_manager=client_manager, logger=logger, - binary=False, json=json, **kwargs) + binary=False, json=json, + async_handlers=async_handlers, **kwargs) def is_asyncio_based(self): return True @@ -280,7 +285,11 @@ class AsyncServer(server.Server): namespace = namespace or '/' self.logger.info('received event "%s" from %s [%s]', data[0], sid, namespace) - await self._handle_event_internal(self, sid, data, namespace, id) + if self.async_handlers: + self.start_background_task(self._handle_event_internal, self, sid, + data, namespace, id) + else: + await self._handle_event_internal(self, sid, data, namespace, id) async def _handle_event_internal(self, server, sid, data, namespace, id): r = await server._trigger_event(data[0], namespace, sid, *data[1:]) diff --git a/tests/test_asyncio_server.py b/tests/test_asyncio_server.py index a19bb38..3be1ac0 100644 --- a/tests/test_asyncio_server.py +++ b/tests/test_asyncio_server.py @@ -56,14 +56,15 @@ class TestAsyncServer(unittest.TestCase): def test_create(self, eio): eio.return_value.handle_request = AsyncMock() mgr = self._get_mock_manager() - s = asyncio_server.AsyncServer(client_manager=mgr, foo='bar') + s = asyncio_server.AsyncServer(client_manager=mgr, + async_handlers=True, foo='bar') _run(s.handle_request({})) _run(s.handle_request({})) eio.assert_called_once_with(**{'foo': 'bar', 'async_handlers': False}) self.assertEqual(s.manager, mgr) self.assertEqual(s.eio.on.call_count, 3) self.assertEqual(s.binary, False) - self.assertEqual(s.async_handlers, False) + self.assertEqual(s.async_handlers, True) def test_attach(self, eio): s = asyncio_server.AsyncServer() @@ -581,12 +582,18 @@ class TestAsyncServer(unittest.TestCase): # restore the default JSON module packet.Packet.json = json + def test_async_handlers(self, eio): + s = asyncio_server.AsyncServer(async_handlers=True) + _run(s._handle_eio_message('123', '2["my message","a","b","c"]')) + s.eio.start_background_task.assert_called_once_with( + s._handle_event_internal, s, '123', ['my message', 'a', 'b', 'c'], + '/', None) + def test_start_background_task(self, eio): - eio.return_value.start_background_task = AsyncMock() s = asyncio_server.AsyncServer() - _run(s.start_background_task('foo', 'bar', baz='baz')) - s.eio.start_background_task.mock.assert_called_once_with('foo', 'bar', - baz='baz') + s.start_background_task('foo', 'bar', baz='baz') + s.eio.start_background_task.assert_called_once_with('foo', 'bar', + baz='baz') def test_sleep(self, eio): eio.return_value.sleep = AsyncMock()