From a5a67120fec2211002915e8bbcff1c034a5eb93f Mon Sep 17 00:00:00 2001 From: Julien Deniau Date: Wed, 16 Jan 2019 10:33:31 +0100 Subject: [PATCH 1/5] fix typo with quotes in doc (#238) --- docs/client.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/client.rst b/docs/client.rst index d929d75..e788d25 100644 --- a/docs/client.rst +++ b/docs/client.rst @@ -45,7 +45,7 @@ functions must be defined using the ``on`` decorator:: @sio.on('connect') def on_connect(): - print('I'm connected!') + print('I\'m connected!') @sio.on('message') def on_message(data): @@ -57,7 +57,7 @@ functions must be defined using the ``on`` decorator:: @sio.on('disconnect') def on_disconnect(): - print('I'm disconnected!') + print('I\'m disconnected!') For the ``asyncio`` server, event handlers can be regular functions as above, or can also be coroutines:: @@ -162,7 +162,7 @@ added to the ``on`` decorator:: @sio.on('connect', namespace='/chat') def on_connect(): - print('I'm connected to the /chat namespace!') + print('I\'m connected to the /chat namespace!') Likewise, the client can emit an event to the server on a namespace by providing its in the ``emit()`` call:: From 3c08dda918dd1985c2f10174e7b54f031908099b Mon Sep 17 00:00:00 2001 From: Emeka Icha Date: Mon, 21 Jan 2019 16:53:21 +0300 Subject: [PATCH 2/5] (fix) small typo in intro doc (#241) --- docs/intro.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/intro.rst b/docs/intro.rst index e05b626..7e862d6 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -67,7 +67,7 @@ asynchronous server: import eventlet sio = socketio.Server() - app = socketio.WSGIApp(eio, static_files={ + app = socketio.WSGIApp(sio, static_files={ '/': {'content_type': 'text/html', 'filename': 'index.html'} }) @@ -125,7 +125,7 @@ Uvicorn web server: Server Features --------------- -- Can connect to servers running other complaint Socket.IO clients besides +- Can connect to servers running other compliant Socket.IO clients besides the one in this package. - Compatible with Python 2.7 and Python 3.5+. - Two versions of the server, one for standard Python and another for From 9423c9f84b85adf38c0f5dab862aa6f07e170ff2 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Wed, 30 Jan 2019 09:34:50 +0000 Subject: [PATCH 3/5] Release 3.1.2 --- socketio/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/socketio/__init__.py b/socketio/__init__.py index 6ede89c..96e24d4 100644 --- a/socketio/__init__.py +++ b/socketio/__init__.py @@ -24,7 +24,7 @@ else: # pragma: no cover AsyncNamespace = None AsyncRedisManager = None -__version__ = '3.1.1' +__version__ = '3.1.2' __all__ = ['__version__', 'Client', 'Server', 'BaseManager', 'PubSubManager', 'KombuManager', 'RedisManager', 'ZmqManager', 'Namespace', From 7c952de346fba6db124cb0ec3bb9853e1ec58482 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Wed, 30 Jan 2019 23:51:30 +0000 Subject: [PATCH 4/5] added wait and timeout options to client's emit --- socketio/asyncio_client.py | 46 +++++++++++++++++++++++++--- socketio/client.py | 44 +++++++++++++++++++++++--- socketio/exceptions.py | 4 +++ tests/asyncio/test_asyncio_client.py | 40 ++++++++++++++++++++++-- tests/common/test_client.py | 43 ++++++++++++++++++++++++-- 5 files changed, 165 insertions(+), 12 deletions(-) diff --git a/socketio/asyncio_client.py b/socketio/asyncio_client.py index 2f78a52..bf733bd 100644 --- a/socketio/asyncio_client.py +++ b/socketio/asyncio_client.py @@ -120,7 +120,8 @@ class AsyncClient(client.Client): if self.eio.state != 'connected': break - async def emit(self, event, data=None, namespace=None, callback=None): + async def emit(self, event, data=None, namespace=None, callback=None, + wait=False, timeout=60): """Emit a custom event to one or more connected clients. :param event: The event name. It can be any string. The event names @@ -137,11 +138,30 @@ class AsyncClient(client.Client): that will be passed to the function are those provided by the client. Callback functions can only be used when addressing an individual client. + :param wait: If set to ``True``, this function will wait for the + server to handle the event and acknowledge it via its + callback function. The value(s) passed by the server to + its callback will be returned. If set to ``False``, + this function emits the event and returns immediately. + :param timeout: If ``wait`` is set to ``True``, this parameter + specifies a waiting timeout. If the timeout is reached + before the server acknowledges the event, then a + ``TimeoutError`` exception is raised. Note: this method is a coroutine. """ namespace = namespace or '/' self.logger.info('Emitting event "%s" [%s]', event, namespace) + if wait is True: + callback_event = self.eio.create_event() + callback_args = [] + + def event_callback(*args): + callback_args.append(args) + callback_event.set() + + callback = event_callback + if callback is not None: id = self._generate_ack_id(namespace, callback) else: @@ -161,8 +181,17 @@ class AsyncClient(client.Client): await self._send_packet(packet.Packet( packet.EVENT, namespace=namespace, data=[event] + data, id=id, binary=binary)) - - async def send(self, data, namespace=None, callback=None): + if wait is True: + try: + await asyncio.wait_for(callback_event.wait(), timeout) + except asyncio.TimeoutError: + six.raise_from(exceptions.TimeoutError(), None) + return callback_args[0] if len(callback_args[0]) > 1 \ + else callback_args[0][0] if len(callback_args[0]) == 1 \ + else None + + async def send(self, data, namespace=None, callback=None, wait=False, + timeout=60): """Send a message to one or more connected clients. This function emits an event with the name ``'message'``. Use @@ -179,11 +208,20 @@ class AsyncClient(client.Client): that will be passed to the function are those provided by the client. Callback functions can only be used when addressing an individual client. + :param wait: If set to ``True``, this function will wait for the + server to handle the event and acknowledge it via its + callback function. The value(s) passed by the server to + its callback will be returned. If set to ``False``, + this function emits the event and returns immediately. + :param timeout: If ``wait`` is set to ``True``, this parameter + specifies a waiting timeout. If the timeout is reached + before the server acknowledges the event, then a + ``TimeoutError`` exception is raised. Note: this method is a coroutine. """ await self.emit('message', data=data, namespace=namespace, - callback=callback) + callback=callback, wait=wait, timeout=timeout) async def disconnect(self): """Disconnect from the server. diff --git a/socketio/client.py b/socketio/client.py index f56493f..8032389 100644 --- a/socketio/client.py +++ b/socketio/client.py @@ -224,7 +224,8 @@ class Client(object): if self.eio.state != 'connected': break - def emit(self, event, data=None, namespace=None, callback=None): + def emit(self, event, data=None, namespace=None, callback=None, + wait=False, timeout=60): """Emit a custom event to one or more connected clients. :param event: The event name. It can be any string. The event names @@ -241,9 +242,28 @@ class Client(object): that will be passed to the function are those provided by the client. Callback functions can only be used when addressing an individual client. + :param wait: If set to ``True``, this function will wait for the + server to handle the event and acknowledge it via its + callback function. The value(s) passed by the server to + its callback will be returned. If set to ``False``, + this function emits the event and returns immediately. + :param timeout: If ``wait`` is set to ``True``, this parameter + specifies a waiting timeout. If the timeout is reached + before the server acknowledges the event, then a + ``TimeoutError`` exception is raised. """ namespace = namespace or '/' self.logger.info('Emitting event "%s" [%s]', event, namespace) + if wait is True: + callback_event = self.eio.create_event() + callback_args = [] + + def event_callback(*args): + callback_args.append(args) + callback_event.set() + + callback = event_callback + if callback is not None: id = self._generate_ack_id(namespace, callback) else: @@ -263,8 +283,15 @@ class Client(object): self._send_packet(packet.Packet(packet.EVENT, namespace=namespace, data=[event] + data, id=id, binary=binary)) - - def send(self, data, namespace=None, callback=None): + if wait is True: + if not callback_event.wait(timeout=timeout): + raise exceptions.TimeoutError() + return callback_args[0] if len(callback_args[0]) > 1 \ + else callback_args[0][0] if len(callback_args[0]) == 1 \ + else None + + def send(self, data, namespace=None, callback=None, wait=False, + timeout=60): """Send a message to one or more connected clients. This function emits an event with the name ``'message'``. Use @@ -281,9 +308,18 @@ class Client(object): that will be passed to the function are those provided by the client. Callback functions can only be used when addressing an individual client. + :param wait: If set to ``True``, this function will wait for the + server to handle the event and acknowledge it via its + callback function. The value(s) passed by the server to + its callback will be returned. If set to ``False``, + this function emits the event and returns immediately. + :param timeout: If ``wait`` is set to ``True``, this parameter + specifies a waiting timeout. If the timeout is reached + before the server acknowledges the event, then a + ``TimeoutError`` exception is raised. """ self.emit('message', data=data, namespace=namespace, - callback=callback) + callback=callback, wait=wait, timeout=timeout) def disconnect(self): """Disconnect from the server.""" diff --git a/socketio/exceptions.py b/socketio/exceptions.py index 5bd8697..eb54efa 100644 --- a/socketio/exceptions.py +++ b/socketio/exceptions.py @@ -4,3 +4,7 @@ class SocketIOError(Exception): class ConnectionError(SocketIOError): pass + + +class TimeoutError(SocketIOError): + pass diff --git a/tests/asyncio/test_asyncio_client.py b/tests/asyncio/test_asyncio_client.py index 78bca0f..14b5f63 100644 --- a/tests/asyncio/test_asyncio_client.py +++ b/tests/asyncio/test_asyncio_client.py @@ -200,6 +200,41 @@ class TestAsyncClient(unittest.TestCase): expected_packet.encode()) c._generate_ack_id.assert_called_once_with('/', 'cb') + def test_emit_with_wait(self): + c = asyncio_client.AsyncClient() + + async def fake_event_wait(): + c._generate_ack_id.call_args_list[0][0][1]('foo', 321) + + c._send_packet = AsyncMock() + c._generate_ack_id = mock.MagicMock(return_value=123) + c.eio = mock.MagicMock() + c.eio.create_event.return_value.wait = fake_event_wait + self.assertEqual(_run(c.emit('foo', wait=True)), ('foo', 321)) + expected_packet = packet.Packet(packet.EVENT, namespace='/', + data=['foo'], id=123, binary=False) + self.assertEqual(c._send_packet.mock.call_count, 1) + self.assertEqual(c._send_packet.mock.call_args_list[0][0][0].encode(), + expected_packet.encode()) + + def test_emit_with_wait_and_timeout(self): + c = asyncio_client.AsyncClient() + + async def fake_event_wait(): + await asyncio.sleep(1) + + c._send_packet = AsyncMock() + c._generate_ack_id = mock.MagicMock(return_value=123) + c.eio = mock.MagicMock() + c.eio.create_event.return_value.wait = fake_event_wait + self.assertRaises(exceptions.TimeoutError, _run, + c.emit('foo', wait=True, timeout=0.01)) + expected_packet = packet.Packet(packet.EVENT, namespace='/', + data=['foo'], id=123, binary=False) + self.assertEqual(c._send_packet.mock.call_count, 1) + self.assertEqual(c._send_packet.mock.call_args_list[0][0][0].encode(), + expected_packet.encode()) + def test_emit_namespace_with_callback(self): c = asyncio_client.AsyncClient() c._send_packet = AsyncMock() @@ -240,14 +275,15 @@ class TestAsyncClient(unittest.TestCase): _run(c.send('data', 'namespace', 'callback')) c.emit.mock.assert_called_once_with( 'message', data='data', namespace='namespace', - callback='callback') + callback='callback', wait=False, timeout=60) def test_send_with_defaults(self): c = asyncio_client.AsyncClient() c.emit = AsyncMock() _run(c.send('data')) c.emit.mock.assert_called_once_with( - 'message', data='data', namespace=None, callback=None) + 'message', data='data', namespace=None, callback=None, wait=False, + timeout=60) def test_disconnect(self): c = asyncio_client.AsyncClient() diff --git a/tests/common/test_client.py b/tests/common/test_client.py index 02d5ed0..2b689b8 100644 --- a/tests/common/test_client.py +++ b/tests/common/test_client.py @@ -283,6 +283,44 @@ class TestClient(unittest.TestCase): expected_packet.encode()) c._generate_ack_id.assert_called_once_with('/', 'cb') + def test_emit_with_wait(self): + c = client.Client() + + def fake_event_wait(timeout=None): + self.assertEqual(timeout, 60) + c._generate_ack_id.call_args_list[0][0][1]('foo', 321) + return True + + c._send_packet = mock.MagicMock() + c._generate_ack_id = mock.MagicMock(return_value=123) + c.eio = mock.MagicMock() + c.eio.create_event.return_value.wait = fake_event_wait + self.assertEqual(c.emit('foo', wait=True), ('foo', 321)) + expected_packet = packet.Packet(packet.EVENT, namespace='/', + data=['foo'], id=123, binary=False) + self.assertEqual(c._send_packet.call_count, 1) + self.assertEqual(c._send_packet.call_args_list[0][0][0].encode(), + expected_packet.encode()) + + def test_emit_with_wait_and_timeout(self): + c = client.Client() + + def fake_event_wait(timeout=None): + self.assertEqual(timeout, 12) + return False + + c._send_packet = mock.MagicMock() + c._generate_ack_id = mock.MagicMock(return_value=123) + c.eio = mock.MagicMock() + c.eio.create_event.return_value.wait = fake_event_wait + self.assertRaises(exceptions.TimeoutError, c.emit, 'foo', wait=True, + timeout=12) + expected_packet = packet.Packet(packet.EVENT, namespace='/', + data=['foo'], id=123, binary=False) + self.assertEqual(c._send_packet.call_count, 1) + self.assertEqual(c._send_packet.call_args_list[0][0][0].encode(), + expected_packet.encode()) + def test_emit_namespace_with_callback(self): c = client.Client() c._send_packet = mock.MagicMock() @@ -323,14 +361,15 @@ class TestClient(unittest.TestCase): c.send('data', 'namespace', 'callback') c.emit.assert_called_once_with( 'message', data='data', namespace='namespace', - callback='callback') + callback='callback', wait=False, timeout=60) def test_send_with_defaults(self): c = client.Client() c.emit = mock.MagicMock() c.send('data') c.emit.assert_called_once_with( - 'message', data='data', namespace=None, callback=None) + 'message', data='data', namespace=None, callback=None, wait=False, + timeout=60) def test_disconnect(self): c = client.Client() From 88dcf7d414baec32d6f9f5571d1c03cb09e75c65 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Sat, 2 Feb 2019 07:53:48 +0000 Subject: [PATCH 5/5] change async_handlers default to true, and add call() method --- socketio/asyncio_client.py | 65 +++++++++++++----------- socketio/asyncio_server.py | 60 +++++++++++++++++++--- socketio/client.py | 59 +++++++++++---------- socketio/server.py | 65 ++++++++++++++++++++---- tests/asyncio/test_asyncio_client.py | 70 ++++++++++++------------- tests/asyncio/test_asyncio_server.py | 72 +++++++++++++++++++------- tests/common/test_client.py | 76 ++++++++++++++-------------- tests/common/test_server.py | 66 ++++++++++++++++++------ 8 files changed, 353 insertions(+), 180 deletions(-) diff --git a/socketio/asyncio_client.py b/socketio/asyncio_client.py index bf733bd..88a6e27 100644 --- a/socketio/asyncio_client.py +++ b/socketio/asyncio_client.py @@ -120,8 +120,7 @@ class AsyncClient(client.Client): if self.eio.state != 'connected': break - async def emit(self, event, data=None, namespace=None, callback=None, - wait=False, timeout=60): + async def emit(self, event, data=None, namespace=None, callback=None): """Emit a custom event to one or more connected clients. :param event: The event name. It can be any string. The event names @@ -138,30 +137,11 @@ class AsyncClient(client.Client): that will be passed to the function are those provided by the client. Callback functions can only be used when addressing an individual client. - :param wait: If set to ``True``, this function will wait for the - server to handle the event and acknowledge it via its - callback function. The value(s) passed by the server to - its callback will be returned. If set to ``False``, - this function emits the event and returns immediately. - :param timeout: If ``wait`` is set to ``True``, this parameter - specifies a waiting timeout. If the timeout is reached - before the server acknowledges the event, then a - ``TimeoutError`` exception is raised. Note: this method is a coroutine. """ namespace = namespace or '/' self.logger.info('Emitting event "%s" [%s]', event, namespace) - if wait is True: - callback_event = self.eio.create_event() - callback_args = [] - - def event_callback(*args): - callback_args.append(args) - callback_event.set() - - callback = event_callback - if callback is not None: id = self._generate_ack_id(namespace, callback) else: @@ -181,14 +161,6 @@ class AsyncClient(client.Client): await self._send_packet(packet.Packet( packet.EVENT, namespace=namespace, data=[event] + data, id=id, binary=binary)) - if wait is True: - try: - await asyncio.wait_for(callback_event.wait(), timeout) - except asyncio.TimeoutError: - six.raise_from(exceptions.TimeoutError(), None) - return callback_args[0] if len(callback_args[0]) > 1 \ - else callback_args[0][0] if len(callback_args[0]) == 1 \ - else None async def send(self, data, namespace=None, callback=None, wait=False, timeout=60): @@ -223,6 +195,41 @@ class AsyncClient(client.Client): await self.emit('message', data=data, namespace=namespace, callback=callback, wait=wait, timeout=timeout) + async def call(self, event, data=None, namespace=None, timeout=60): + """Emit a custom event to a client and wait for the response. + + :param event: The event name. It can be any string. The event names + ``'connect'``, ``'message'`` and ``'disconnect'`` are + reserved and should not be used. + :param data: The data to send to the client or clients. Data can be of + type ``str``, ``bytes``, ``list`` or ``dict``. If a + ``list`` or ``dict``, the data will be serialized as JSON. + :param namespace: The Socket.IO namespace for the event. If this + argument is omitted the event is emitted to the + default namespace. + :param timeout: The waiting timeout. If the timeout is reached before + the client acknowledges the event, then a + ``TimeoutError`` exception is raised. + + Note: this method is a coroutine. + """ + callback_event = self.eio.create_event() + callback_args = [] + + def event_callback(*args): + callback_args.append(args) + callback_event.set() + + await self.emit(event, data=data, namespace=namespace, + callback=event_callback) + try: + await asyncio.wait_for(callback_event.wait(), timeout) + except asyncio.TimeoutError: + six.raise_from(exceptions.TimeoutError(), None) + return callback_args[0] if len(callback_args[0]) > 1 \ + else callback_args[0][0] if len(callback_args[0]) == 1 \ + else None + async def disconnect(self): """Disconnect from the server. diff --git a/socketio/asyncio_server.py b/socketio/asyncio_server.py index cbd812b..144b79c 100644 --- a/socketio/asyncio_server.py +++ b/socketio/asyncio_server.py @@ -1,8 +1,10 @@ import asyncio import engineio +import six from . import asyncio_manager +from . import exceptions from . import packet from . import server @@ -26,7 +28,7 @@ class AsyncServer(server.Server): 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``. + set to ``False``. The default is ``True``. :param kwargs: Connection parameters for the underlying Engine.IO server. The Engine.IO configuration supports the following settings: @@ -59,7 +61,7 @@ class AsyncServer(server.Server): ``False``. """ def __init__(self, client_manager=None, logger=False, json=None, - async_handlers=False, **kwargs): + async_handlers=True, **kwargs): if client_manager is None: client_manager = asyncio_manager.AsyncManager() super().__init__(client_manager=client_manager, logger=logger, @@ -112,8 +114,9 @@ class AsyncServer(server.Server): namespace = namespace or '/' self.logger.info('emitting event "%s" to %s [%s]', event, room or 'all', namespace) - await self.manager.emit(event, data, namespace, room, skip_sid, - callback, **kwargs) + await self.manager.emit(event, data, namespace, room=room, + skip_sid=skip_sid, callback=callback, + **kwargs) async def send(self, data, room=None, skip_sid=None, namespace=None, callback=None, **kwargs): @@ -151,9 +154,54 @@ class AsyncServer(server.Server): Note: this method is a coroutine. """ - await self.emit('message', data, room, skip_sid, namespace, callback, - **kwargs) + await self.emit('message', data=data, room=room, skip_sid=skip_sid, + namespace=namespace, callback=callback, **kwargs) + async def call(self, event, data=None, sid=None, namespace=None, + timeout=60, **kwargs): + """Emit a custom event to a client and wait for the response. + + :param event: The event name. It can be any string. The event names + ``'connect'``, ``'message'`` and ``'disconnect'`` are + reserved and should not be used. + :param data: The data to send to the client or clients. Data can be of + type ``str``, ``bytes``, ``list`` or ``dict``. If a + ``list`` or ``dict``, the data will be serialized as JSON. + :param sid: The session ID of the recipient client. + :param namespace: The Socket.IO namespace for the event. If this + argument is omitted the event is emitted to the + default namespace. + :param timeout: The waiting timeout. If the timeout is reached before + the client acknowledges the event, then a + ``TimeoutError`` exception is raised. + :param ignore_queue: Only used when a message queue is configured. If + set to ``True``, the event is emitted to the + client directly, without going through the queue. + This is more efficient, but only works when a + single server process is used. It is recommended + to always leave this parameter with its default + value of ``False``. + """ + if not self.async_handlers: + raise RuntimeError( + 'Cannot use call() when async_handlers is False.') + callback_event = self.eio.create_event() + callback_args = [] + + def event_callback(*args): + callback_args.append(args) + callback_event.set() + + await self.emit(event, data=data, room=sid, namespace=namespace, + callback=event_callback, **kwargs) + try: + await asyncio.wait_for(callback_event.wait(), timeout) + except asyncio.TimeoutError: + six.raise_from(exceptions.TimeoutError(), None) + return callback_args[0] if len(callback_args[0]) > 1 \ + else callback_args[0][0] if len(callback_args[0]) == 1 \ + else None + async def close_room(self, room, namespace=None): """Close a room. diff --git a/socketio/client.py b/socketio/client.py index 8032389..84e13ef 100644 --- a/socketio/client.py +++ b/socketio/client.py @@ -224,8 +224,7 @@ class Client(object): if self.eio.state != 'connected': break - def emit(self, event, data=None, namespace=None, callback=None, - wait=False, timeout=60): + def emit(self, event, data=None, namespace=None, callback=None): """Emit a custom event to one or more connected clients. :param event: The event name. It can be any string. The event names @@ -242,28 +241,9 @@ class Client(object): that will be passed to the function are those provided by the client. Callback functions can only be used when addressing an individual client. - :param wait: If set to ``True``, this function will wait for the - server to handle the event and acknowledge it via its - callback function. The value(s) passed by the server to - its callback will be returned. If set to ``False``, - this function emits the event and returns immediately. - :param timeout: If ``wait`` is set to ``True``, this parameter - specifies a waiting timeout. If the timeout is reached - before the server acknowledges the event, then a - ``TimeoutError`` exception is raised. """ namespace = namespace or '/' self.logger.info('Emitting event "%s" [%s]', event, namespace) - if wait is True: - callback_event = self.eio.create_event() - callback_args = [] - - def event_callback(*args): - callback_args.append(args) - callback_event.set() - - callback = event_callback - if callback is not None: id = self._generate_ack_id(namespace, callback) else: @@ -283,12 +263,6 @@ class Client(object): self._send_packet(packet.Packet(packet.EVENT, namespace=namespace, data=[event] + data, id=id, binary=binary)) - if wait is True: - if not callback_event.wait(timeout=timeout): - raise exceptions.TimeoutError() - return callback_args[0] if len(callback_args[0]) > 1 \ - else callback_args[0][0] if len(callback_args[0]) == 1 \ - else None def send(self, data, namespace=None, callback=None, wait=False, timeout=60): @@ -321,6 +295,37 @@ class Client(object): self.emit('message', data=data, namespace=namespace, callback=callback, wait=wait, timeout=timeout) + def call(self, event, data=None, namespace=None, timeout=60): + """Emit a custom event to a client and wait for the response. + + :param event: The event name. It can be any string. The event names + ``'connect'``, ``'message'`` and ``'disconnect'`` are + reserved and should not be used. + :param data: The data to send to the client or clients. Data can be of + type ``str``, ``bytes``, ``list`` or ``dict``. If a + ``list`` or ``dict``, the data will be serialized as JSON. + :param namespace: The Socket.IO namespace for the event. If this + argument is omitted the event is emitted to the + default namespace. + :param timeout: The waiting timeout. If the timeout is reached before + the client acknowledges the event, then a + ``TimeoutError`` exception is raised. + """ + callback_event = self.eio.create_event() + callback_args = [] + + def event_callback(*args): + callback_args.append(args) + callback_event.set() + + self.emit(event, data=data, namespace=namespace, + callback=event_callback) + if not callback_event.wait(timeout=timeout): + raise exceptions.TimeoutError() + return callback_args[0] if len(callback_args[0]) > 1 \ + else callback_args[0][0] if len(callback_args[0]) == 1 \ + else None + def disconnect(self): """Disconnect from the server.""" for n in self.namespaces: diff --git a/socketio/server.py b/socketio/server.py index 449c94a..8157411 100644 --- a/socketio/server.py +++ b/socketio/server.py @@ -4,8 +4,9 @@ import engineio import six from . import base_manager -from . import packet +from . import exceptions from . import namespace +from . import packet default_logger = logging.getLogger('socketio.server') @@ -33,9 +34,10 @@ class Server(object): 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 async_handlers: If set to ``True``, event handlers for a client are + executed in separate threads. To run handlers for a + client synchronously, set to ``False``. The default + is ``True``. :param kwargs: Connection parameters for the underlying Engine.IO server. The Engine.IO configuration supports the following settings: @@ -77,7 +79,7 @@ class Server(object): ``False``. The default is ``False``. """ def __init__(self, client_manager=None, logger=False, binary=False, - json=None, async_handlers=False, **kwargs): + json=None, async_handlers=True, **kwargs): engineio_options = kwargs engineio_logger = engineio_options.pop('engineio_logger', None) if engineio_logger is not None: @@ -224,11 +226,11 @@ class Server(object): namespace = namespace or '/' self.logger.info('emitting event "%s" to %s [%s]', event, room or 'all', namespace) - self.manager.emit(event, data, namespace, room, skip_sid, callback, - **kwargs) + self.manager.emit(event, data, namespace, room=room, + skip_sid=skip_sid, callback=callback, **kwargs) def send(self, data, room=None, skip_sid=None, namespace=None, - callback=None, **kwargs): + callback=None, wait=False, timeout=60, **kwargs): """Send a message to one or more connected clients. This function emits an event with the name ``'message'``. Use @@ -261,8 +263,51 @@ class Server(object): to always leave this parameter with its default value of ``False``. """ - self.emit('message', data, room, skip_sid, namespace, callback, - **kwargs) + self.emit('message', data=data, room=room, skip_sid=skip_sid, + namespace=namespace, callback=callback, **kwargs) + + def call(self, event, data=None, sid=None, namespace=None, timeout=60, + **kwargs): + """Emit a custom event to a client and wait for the response. + + :param event: The event name. It can be any string. The event names + ``'connect'``, ``'message'`` and ``'disconnect'`` are + reserved and should not be used. + :param data: The data to send to the client or clients. Data can be of + type ``str``, ``bytes``, ``list`` or ``dict``. If a + ``list`` or ``dict``, the data will be serialized as JSON. + :param sid: The session ID of the recipient client. + :param namespace: The Socket.IO namespace for the event. If this + argument is omitted the event is emitted to the + default namespace. + :param timeout: The waiting timeout. If the timeout is reached before + the client acknowledges the event, then a + ``TimeoutError`` exception is raised. + :param ignore_queue: Only used when a message queue is configured. If + set to ``True``, the event is emitted to the + client directly, without going through the queue. + This is more efficient, but only works when a + single server process is used. It is recommended + to always leave this parameter with its default + value of ``False``. + """ + if not self.async_handlers: + raise RuntimeError( + 'Cannot use call() when async_handlers is False.') + callback_event = self.eio.create_event() + callback_args = [] + + def event_callback(*args): + callback_args.append(args) + callback_event.set() + + self.emit(event, data=data, room=sid, namespace=namespace, + callback=event_callback, **kwargs) + if not callback_event.wait(timeout=timeout): + raise exceptions.TimeoutError() + return callback_args[0] if len(callback_args[0]) > 1 \ + else callback_args[0][0] if len(callback_args[0]) == 1 \ + else None def enter_room(self, sid, room, namespace=None): """Enter a room. diff --git a/tests/asyncio/test_asyncio_client.py b/tests/asyncio/test_asyncio_client.py index 14b5f63..3114443 100644 --- a/tests/asyncio/test_asyncio_client.py +++ b/tests/asyncio/test_asyncio_client.py @@ -200,41 +200,6 @@ class TestAsyncClient(unittest.TestCase): expected_packet.encode()) c._generate_ack_id.assert_called_once_with('/', 'cb') - def test_emit_with_wait(self): - c = asyncio_client.AsyncClient() - - async def fake_event_wait(): - c._generate_ack_id.call_args_list[0][0][1]('foo', 321) - - c._send_packet = AsyncMock() - c._generate_ack_id = mock.MagicMock(return_value=123) - c.eio = mock.MagicMock() - c.eio.create_event.return_value.wait = fake_event_wait - self.assertEqual(_run(c.emit('foo', wait=True)), ('foo', 321)) - expected_packet = packet.Packet(packet.EVENT, namespace='/', - data=['foo'], id=123, binary=False) - self.assertEqual(c._send_packet.mock.call_count, 1) - self.assertEqual(c._send_packet.mock.call_args_list[0][0][0].encode(), - expected_packet.encode()) - - def test_emit_with_wait_and_timeout(self): - c = asyncio_client.AsyncClient() - - async def fake_event_wait(): - await asyncio.sleep(1) - - c._send_packet = AsyncMock() - c._generate_ack_id = mock.MagicMock(return_value=123) - c.eio = mock.MagicMock() - c.eio.create_event.return_value.wait = fake_event_wait - self.assertRaises(exceptions.TimeoutError, _run, - c.emit('foo', wait=True, timeout=0.01)) - expected_packet = packet.Packet(packet.EVENT, namespace='/', - data=['foo'], id=123, binary=False) - self.assertEqual(c._send_packet.mock.call_count, 1) - self.assertEqual(c._send_packet.mock.call_args_list[0][0][0].encode(), - expected_packet.encode()) - def test_emit_namespace_with_callback(self): c = asyncio_client.AsyncClient() c._send_packet = AsyncMock() @@ -285,6 +250,41 @@ class TestAsyncClient(unittest.TestCase): 'message', data='data', namespace=None, callback=None, wait=False, timeout=60) + def test_call(self): + c = asyncio_client.AsyncClient() + + async def fake_event_wait(): + c._generate_ack_id.call_args_list[0][0][1]('foo', 321) + + c._send_packet = AsyncMock() + c._generate_ack_id = mock.MagicMock(return_value=123) + c.eio = mock.MagicMock() + c.eio.create_event.return_value.wait = fake_event_wait + self.assertEqual(_run(c.call('foo')), ('foo', 321)) + expected_packet = packet.Packet(packet.EVENT, namespace='/', + data=['foo'], id=123, binary=False) + self.assertEqual(c._send_packet.mock.call_count, 1) + self.assertEqual(c._send_packet.mock.call_args_list[0][0][0].encode(), + expected_packet.encode()) + + def test_call_with_timeout(self): + c = asyncio_client.AsyncClient() + + async def fake_event_wait(): + await asyncio.sleep(1) + + c._send_packet = AsyncMock() + c._generate_ack_id = mock.MagicMock(return_value=123) + c.eio = mock.MagicMock() + c.eio.create_event.return_value.wait = fake_event_wait + self.assertRaises(exceptions.TimeoutError, _run, + c.call('foo', timeout=0.01)) + expected_packet = packet.Packet(packet.EVENT, namespace='/', + data=['foo'], id=123, binary=False) + self.assertEqual(c._send_packet.mock.call_count, 1) + self.assertEqual(c._send_packet.mock.call_args_list[0][0][0].encode(), + expected_packet.encode()) + def test_disconnect(self): c = asyncio_client.AsyncClient() c._trigger_event = AsyncMock() diff --git a/tests/asyncio/test_asyncio_server.py b/tests/asyncio/test_asyncio_server.py index 4761050..fc855c6 100644 --- a/tests/asyncio/test_asyncio_server.py +++ b/tests/asyncio/test_asyncio_server.py @@ -12,8 +12,9 @@ else: from socketio import asyncio_server from socketio import asyncio_namespace -from socketio import packet +from socketio import exceptions from socketio import namespace +from socketio import packet def AsyncMock(*args, **kwargs): @@ -83,24 +84,57 @@ class TestAsyncServer(unittest.TestCase): def test_emit(self, eio): mgr = self._get_mock_manager() s = asyncio_server.AsyncServer(client_manager=mgr) - _run(s.emit('my event', {'foo': 'bar'}, 'room', '123', - namespace='/foo', callback='cb')) + _run(s.emit('my event', {'foo': 'bar'}, room='room', + skip_sid='123', namespace='/foo', callback='cb')) s.manager.emit.mock.assert_called_once_with( - 'my event', {'foo': 'bar'}, '/foo', 'room', '123', 'cb') + 'my event', {'foo': 'bar'}, '/foo', room='room', skip_sid='123', + callback='cb') def test_emit_default_namespace(self, eio): mgr = self._get_mock_manager() s = asyncio_server.AsyncServer(client_manager=mgr) - _run(s.emit('my event', {'foo': 'bar'}, 'room', '123', callback='cb')) - s.manager.emit.mock.assert_called_once_with('my event', {'foo': 'bar'}, - '/', 'room', '123', 'cb') + _run(s.emit('my event', {'foo': 'bar'}, room='room', + skip_sid='123', callback='cb')) + s.manager.emit.mock.assert_called_once_with( + 'my event', {'foo': 'bar'}, '/', room='room', skip_sid='123', + callback='cb') def test_send(self, eio): mgr = self._get_mock_manager() s = asyncio_server.AsyncServer(client_manager=mgr) _run(s.send('foo', 'room', '123', namespace='/foo', callback='cb')) - s.manager.emit.mock.assert_called_once_with('message', 'foo', '/foo', - 'room', '123', 'cb') + s.manager.emit.mock.assert_called_once_with( + 'message', 'foo', '/foo', room='room', skip_sid='123', + callback='cb') + + def test_call(self, eio): + mgr = self._get_mock_manager() + s = asyncio_server.AsyncServer(client_manager=mgr) + + async def fake_event_wait(): + s.manager.emit.mock.call_args_list[0][1]['callback']('foo', 321) + return True + + s.eio.create_event.return_value.wait = fake_event_wait + self.assertEqual(_run(s.call('foo', sid='123')), ('foo', 321)) + + def test_call_with_timeout(self, eio): + mgr = self._get_mock_manager() + s = asyncio_server.AsyncServer(client_manager=mgr) + + async def fake_event_wait(): + await asyncio.sleep(1) + + s.eio.create_event.return_value.wait = fake_event_wait + self.assertRaises(exceptions.TimeoutError, _run, + s.call('foo', sid='123', timeout=0.01)) + + def test_call_without_async_handlers(self, eio): + mgr = self._get_mock_manager() + s = asyncio_server.AsyncServer(client_manager=mgr, + async_handlers=False) + self.assertRaises(RuntimeError, _run, + s.call('foo', sid='123', timeout=12)) def test_enter_room(self, eio): mgr = self._get_mock_manager() @@ -327,7 +361,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event(self, eio): eio.return_value.send = AsyncMock() - s = asyncio_server.AsyncServer() + s = asyncio_server.AsyncServer(async_handlers=False) handler = AsyncMock() s.on('my message', handler) _run(s._handle_eio_message('123', '2["my message","a","b","c"]')) @@ -335,7 +369,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event_with_namespace(self, eio): eio.return_value.send = AsyncMock() - s = asyncio_server.AsyncServer() + s = asyncio_server.AsyncServer(async_handlers=False) handler = mock.MagicMock() s.on('my message', handler, namespace='/foo') _run(s._handle_eio_message('123', '2/foo,["my message","a","b","c"]')) @@ -343,7 +377,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event_binary(self, eio): eio.return_value.send = AsyncMock() - s = asyncio_server.AsyncServer() + s = asyncio_server.AsyncServer(async_handlers=False) handler = mock.MagicMock() s.on('my message', handler) _run(s._handle_eio_message('123', '52-["my message","a",' @@ -356,7 +390,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event_binary_ack(self, eio): eio.return_value.send = AsyncMock() mgr = self._get_mock_manager() - s = asyncio_server.AsyncServer(client_manager=mgr) + s = asyncio_server.AsyncServer(client_manager=mgr, async_handlers=False) s.manager.initialize(s) _run(s._handle_eio_message('123', '61-321["my message","a",' '{"_placeholder":true,"num":0}]')) @@ -366,7 +400,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event_with_ack(self, eio): eio.return_value.send = AsyncMock() - s = asyncio_server.AsyncServer() + s = asyncio_server.AsyncServer(async_handlers=False) handler = mock.MagicMock(return_value='foo') s.on('my message', handler) _run(s._handle_eio_message('123', '21000["my message","foo"]')) @@ -376,7 +410,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event_with_ack_none(self, eio): eio.return_value.send = AsyncMock() - s = asyncio_server.AsyncServer() + s = asyncio_server.AsyncServer(async_handlers=False) handler = mock.MagicMock(return_value=None) s.on('my message', handler) _run(s._handle_eio_message('123', '21000["my message","foo"]')) @@ -387,7 +421,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event_with_ack_tuple(self, eio): eio.return_value.send = AsyncMock() mgr = self._get_mock_manager() - s = asyncio_server.AsyncServer(client_manager=mgr) + s = asyncio_server.AsyncServer(client_manager=mgr, async_handlers=False) handler = mock.MagicMock(return_value=(1, '2', True)) s.on('my message', handler) _run(s._handle_eio_message('123', '21000["my message","a","b","c"]')) @@ -398,7 +432,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event_with_ack_list(self, eio): eio.return_value.send = AsyncMock() mgr = self._get_mock_manager() - s = asyncio_server.AsyncServer(client_manager=mgr) + s = asyncio_server.AsyncServer(client_manager=mgr, async_handlers=False) handler = mock.MagicMock(return_value=[1, '2', True]) s.on('my message', handler) _run(s._handle_eio_message('123', '21000["my message","a","b","c"]')) @@ -409,7 +443,7 @@ class TestAsyncServer(unittest.TestCase): def test_handle_event_with_ack_binary(self, eio): eio.return_value.send = AsyncMock() mgr = self._get_mock_manager() - s = asyncio_server.AsyncServer(client_manager=mgr) + s = asyncio_server.AsyncServer(client_manager=mgr, async_handlers=False) handler = mock.MagicMock(return_value=b'foo') s.on('my message', handler) _run(s._handle_eio_message('123', '21000["my message","foo"]')) @@ -536,7 +570,7 @@ class TestAsyncServer(unittest.TestCase): async def on_baz(self, sid, data1, data2): result['result'] = (data1, data2) - s = asyncio_server.AsyncServer() + s = asyncio_server.AsyncServer(async_handlers=False) s.register_namespace(MyNamespace('/foo')) _run(s._handle_eio_connect('123', 'environ')) _run(s._handle_eio_message('123', '0/foo')) diff --git a/tests/common/test_client.py b/tests/common/test_client.py index 2b689b8..8fd078d 100644 --- a/tests/common/test_client.py +++ b/tests/common/test_client.py @@ -283,44 +283,6 @@ class TestClient(unittest.TestCase): expected_packet.encode()) c._generate_ack_id.assert_called_once_with('/', 'cb') - def test_emit_with_wait(self): - c = client.Client() - - def fake_event_wait(timeout=None): - self.assertEqual(timeout, 60) - c._generate_ack_id.call_args_list[0][0][1]('foo', 321) - return True - - c._send_packet = mock.MagicMock() - c._generate_ack_id = mock.MagicMock(return_value=123) - c.eio = mock.MagicMock() - c.eio.create_event.return_value.wait = fake_event_wait - self.assertEqual(c.emit('foo', wait=True), ('foo', 321)) - expected_packet = packet.Packet(packet.EVENT, namespace='/', - data=['foo'], id=123, binary=False) - self.assertEqual(c._send_packet.call_count, 1) - self.assertEqual(c._send_packet.call_args_list[0][0][0].encode(), - expected_packet.encode()) - - def test_emit_with_wait_and_timeout(self): - c = client.Client() - - def fake_event_wait(timeout=None): - self.assertEqual(timeout, 12) - return False - - c._send_packet = mock.MagicMock() - c._generate_ack_id = mock.MagicMock(return_value=123) - c.eio = mock.MagicMock() - c.eio.create_event.return_value.wait = fake_event_wait - self.assertRaises(exceptions.TimeoutError, c.emit, 'foo', wait=True, - timeout=12) - expected_packet = packet.Packet(packet.EVENT, namespace='/', - data=['foo'], id=123, binary=False) - self.assertEqual(c._send_packet.call_count, 1) - self.assertEqual(c._send_packet.call_args_list[0][0][0].encode(), - expected_packet.encode()) - def test_emit_namespace_with_callback(self): c = client.Client() c._send_packet = mock.MagicMock() @@ -371,6 +333,44 @@ class TestClient(unittest.TestCase): 'message', data='data', namespace=None, callback=None, wait=False, timeout=60) + def test_call(self): + c = client.Client() + + def fake_event_wait(timeout=None): + self.assertEqual(timeout, 60) + c._generate_ack_id.call_args_list[0][0][1]('foo', 321) + return True + + c._send_packet = mock.MagicMock() + c._generate_ack_id = mock.MagicMock(return_value=123) + c.eio = mock.MagicMock() + c.eio.create_event.return_value.wait = fake_event_wait + self.assertEqual(c.call('foo'), ('foo', 321)) + expected_packet = packet.Packet(packet.EVENT, namespace='/', + data=['foo'], id=123, binary=False) + self.assertEqual(c._send_packet.call_count, 1) + self.assertEqual(c._send_packet.call_args_list[0][0][0].encode(), + expected_packet.encode()) + + def test_call_with_timeout(self): + c = client.Client() + + def fake_event_wait(timeout=None): + self.assertEqual(timeout, 12) + return False + + c._send_packet = mock.MagicMock() + c._generate_ack_id = mock.MagicMock(return_value=123) + c.eio = mock.MagicMock() + c.eio.create_event.return_value.wait = fake_event_wait + self.assertRaises(exceptions.TimeoutError, c.call, 'foo', + timeout=12) + expected_packet = packet.Packet(packet.EVENT, namespace='/', + data=['foo'], id=123, binary=False) + self.assertEqual(c._send_packet.call_count, 1) + self.assertEqual(c._send_packet.call_args_list[0][0][0].encode(), + expected_packet.encode()) + def test_disconnect(self): c = client.Client() c._trigger_event = mock.MagicMock() diff --git a/tests/common/test_server.py b/tests/common/test_server.py index 5e16ec5..6a54f6f 100644 --- a/tests/common/test_server.py +++ b/tests/common/test_server.py @@ -8,9 +8,10 @@ if six.PY3: else: import mock +from socketio import exceptions +from socketio import namespace from socketio import packet from socketio import server -from socketio import namespace @mock.patch('engineio.Server') @@ -52,22 +53,55 @@ class TestServer(unittest.TestCase): s = server.Server(client_manager=mgr) s.emit('my event', {'foo': 'bar'}, 'room', '123', namespace='/foo', callback='cb') - s.manager.emit.assert_called_once_with('my event', {'foo': 'bar'}, - '/foo', 'room', '123', 'cb') + s.manager.emit.assert_called_once_with( + 'my event', {'foo': 'bar'}, '/foo', room='room', skip_sid='123', + callback='cb') def test_emit_default_namespace(self, eio): mgr = mock.MagicMock() s = server.Server(client_manager=mgr) s.emit('my event', {'foo': 'bar'}, 'room', '123', callback='cb') - s.manager.emit.assert_called_once_with('my event', {'foo': 'bar'}, '/', - 'room', '123', 'cb') + s.manager.emit.assert_called_once_with( + 'my event', {'foo': 'bar'}, '/', room='room', skip_sid='123', + callback='cb') def test_send(self, eio): mgr = mock.MagicMock() s = server.Server(client_manager=mgr) s.send('foo', 'room', '123', namespace='/foo', callback='cb') - s.manager.emit.assert_called_once_with('message', 'foo', '/foo', - 'room', '123', 'cb') + s.manager.emit.assert_called_once_with( + 'message', 'foo', '/foo', room='room', skip_sid='123', + callback='cb') + + def test_call(self, eio): + mgr = mock.MagicMock() + s = server.Server(client_manager=mgr) + + def fake_event_wait(timeout=None): + self.assertEqual(timeout, 60) + s.manager.emit.call_args_list[0][1]['callback']('foo', 321) + return True + + s.eio.create_event.return_value.wait = fake_event_wait + self.assertEqual(s.call('foo', sid='123'), ('foo', 321)) + + def test_call_with_timeout(self, eio): + mgr = mock.MagicMock() + s = server.Server(client_manager=mgr) + + def fake_event_wait(timeout=None): + self.assertEqual(timeout, 12) + return False + + s.eio.create_event.return_value.wait = fake_event_wait + self.assertRaises(exceptions.TimeoutError, s.call, 'foo', + sid='123', timeout=12) + + def test_call_without_async_handlers(self, eio): + mgr = mock.MagicMock() + s = server.Server(client_manager=mgr, async_handlers=False) + self.assertRaises(RuntimeError, s.call, 'foo', + sid='123', timeout=12) def test_enter_room(self, eio): mgr = mock.MagicMock() @@ -265,21 +299,21 @@ class TestServer(unittest.TestCase): s._handle_eio_disconnect('123') def test_handle_event(self, eio): - s = server.Server() + s = server.Server(async_handlers=False) handler = mock.MagicMock() s.on('my message', handler) s._handle_eio_message('123', '2["my message","a","b","c"]') handler.assert_called_once_with('123', 'a', 'b', 'c') def test_handle_event_with_namespace(self, eio): - s = server.Server() + s = server.Server(async_handlers=False) handler = mock.MagicMock() s.on('my message', handler, namespace='/foo') s._handle_eio_message('123', '2/foo,["my message","a","b","c"]') handler.assert_called_once_with('123', 'a', 'b', 'c') def test_handle_event_binary(self, eio): - s = server.Server() + s = server.Server(async_handlers=False) handler = mock.MagicMock() s.on('my message', handler) s._handle_eio_message('123', '52-["my message","a",' @@ -300,7 +334,7 @@ class TestServer(unittest.TestCase): '123', '/', 321, ['my message', 'a', b'foo']) def test_handle_event_with_ack(self, eio): - s = server.Server() + s = server.Server(async_handlers=False) handler = mock.MagicMock(return_value='foo') s.on('my message', handler) s._handle_eio_message('123', '21000["my message","foo"]') @@ -309,7 +343,7 @@ class TestServer(unittest.TestCase): binary=False) def test_handle_event_with_ack_none(self, eio): - s = server.Server() + s = server.Server(async_handlers=False) handler = mock.MagicMock(return_value=None) s.on('my message', handler) s._handle_eio_message('123', '21000["my message","foo"]') @@ -319,7 +353,7 @@ class TestServer(unittest.TestCase): def test_handle_event_with_ack_tuple(self, eio): mgr = mock.MagicMock() - s = server.Server(client_manager=mgr) + s = server.Server(client_manager=mgr, async_handlers=False) handler = mock.MagicMock(return_value=(1, '2', True)) s.on('my message', handler) s._handle_eio_message('123', '21000["my message","a","b","c"]') @@ -329,7 +363,7 @@ class TestServer(unittest.TestCase): def test_handle_event_with_ack_list(self, eio): mgr = mock.MagicMock() - s = server.Server(client_manager=mgr) + s = server.Server(client_manager=mgr, async_handlers=False) handler = mock.MagicMock(return_value=[1, '2', True]) s.on('my message', handler) s._handle_eio_message('123', '21000["my message","a","b","c"]') @@ -339,7 +373,7 @@ class TestServer(unittest.TestCase): def test_handle_event_with_ack_binary(self, eio): mgr = mock.MagicMock() - s = server.Server(client_manager=mgr, binary=True) + s = server.Server(client_manager=mgr, binary=True, async_handlers=False) handler = mock.MagicMock(return_value=b'foo') s.on('my message', handler) s._handle_eio_message('123', '21000["my message","foo"]') @@ -452,7 +486,7 @@ class TestServer(unittest.TestCase): def on_baz(self, sid, data1, data2): result['result'] = (data1, data2) - s = server.Server() + s = server.Server(async_handlers=False) s.register_namespace(MyNamespace('/foo')) s._handle_eio_connect('123', 'environ') s._handle_eio_message('123', '0/foo')