Browse Source

change async_handlers default to true, and add call() method

pull/247/head
Miguel Grinberg 6 years ago
parent
commit
88dcf7d414
No known key found for this signature in database GPG Key ID: 36848B262DF5F06C
  1. 65
      socketio/asyncio_client.py
  2. 60
      socketio/asyncio_server.py
  3. 59
      socketio/client.py
  4. 65
      socketio/server.py
  5. 70
      tests/asyncio/test_asyncio_client.py
  6. 72
      tests/asyncio/test_asyncio_server.py
  7. 76
      tests/common/test_client.py
  8. 66
      tests/common/test_server.py

65
socketio/asyncio_client.py

@ -120,8 +120,7 @@ class AsyncClient(client.Client):
if self.eio.state != 'connected': if self.eio.state != 'connected':
break 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. """Emit a custom event to one or more connected clients.
:param event: The event name. It can be any string. The event names :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 that will be passed to the function are those provided
by the client. Callback functions can only be used by the client. Callback functions can only be used
when addressing an individual client. 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. Note: this method is a coroutine.
""" """
namespace = namespace or '/' namespace = namespace or '/'
self.logger.info('Emitting event "%s" [%s]', event, namespace) 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: if callback is not None:
id = self._generate_ack_id(namespace, callback) id = self._generate_ack_id(namespace, callback)
else: else:
@ -181,14 +161,6 @@ class AsyncClient(client.Client):
await self._send_packet(packet.Packet( await self._send_packet(packet.Packet(
packet.EVENT, namespace=namespace, data=[event] + data, id=id, packet.EVENT, namespace=namespace, data=[event] + data, id=id,
binary=binary)) 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, async def send(self, data, namespace=None, callback=None, wait=False,
timeout=60): timeout=60):
@ -223,6 +195,41 @@ class AsyncClient(client.Client):
await self.emit('message', data=data, namespace=namespace, await self.emit('message', data=data, namespace=namespace,
callback=callback, wait=wait, timeout=timeout) 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): async def disconnect(self):
"""Disconnect from the server. """Disconnect from the server.

60
socketio/asyncio_server.py

@ -1,8 +1,10 @@
import asyncio import asyncio
import engineio import engineio
import six
from . import asyncio_manager from . import asyncio_manager
from . import exceptions
from . import packet from . import packet
from . import server from . import server
@ -26,7 +28,7 @@ class AsyncServer(server.Server):
versions. versions.
:param async_handlers: If set to ``True``, event handlers are executed in :param async_handlers: If set to ``True``, event handlers are executed in
separate threads. To run handlers synchronously, 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. :param kwargs: Connection parameters for the underlying Engine.IO server.
The Engine.IO configuration supports the following settings: The Engine.IO configuration supports the following settings:
@ -59,7 +61,7 @@ class AsyncServer(server.Server):
``False``. ``False``.
""" """
def __init__(self, client_manager=None, logger=False, json=None, def __init__(self, client_manager=None, logger=False, json=None,
async_handlers=False, **kwargs): async_handlers=True, **kwargs):
if client_manager is None: if client_manager is None:
client_manager = asyncio_manager.AsyncManager() client_manager = asyncio_manager.AsyncManager()
super().__init__(client_manager=client_manager, logger=logger, super().__init__(client_manager=client_manager, logger=logger,
@ -112,8 +114,9 @@ class AsyncServer(server.Server):
namespace = namespace or '/' namespace = namespace or '/'
self.logger.info('emitting event "%s" to %s [%s]', event, self.logger.info('emitting event "%s" to %s [%s]', event,
room or 'all', namespace) room or 'all', namespace)
await self.manager.emit(event, data, namespace, room, skip_sid, await self.manager.emit(event, data, namespace, room=room,
callback, **kwargs) skip_sid=skip_sid, callback=callback,
**kwargs)
async def send(self, data, room=None, skip_sid=None, namespace=None, async def send(self, data, room=None, skip_sid=None, namespace=None,
callback=None, **kwargs): callback=None, **kwargs):
@ -151,9 +154,54 @@ class AsyncServer(server.Server):
Note: this method is a coroutine. Note: this method is a coroutine.
""" """
await self.emit('message', data, room, skip_sid, namespace, callback, await self.emit('message', data=data, room=room, skip_sid=skip_sid,
**kwargs) 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): async def close_room(self, room, namespace=None):
"""Close a room. """Close a room.

59
socketio/client.py

@ -224,8 +224,7 @@ class Client(object):
if self.eio.state != 'connected': if self.eio.state != 'connected':
break 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. """Emit a custom event to one or more connected clients.
:param event: The event name. It can be any string. The event names :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 that will be passed to the function are those provided
by the client. Callback functions can only be used by the client. Callback functions can only be used
when addressing an individual client. 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 '/' namespace = namespace or '/'
self.logger.info('Emitting event "%s" [%s]', event, namespace) 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: if callback is not None:
id = self._generate_ack_id(namespace, callback) id = self._generate_ack_id(namespace, callback)
else: else:
@ -283,12 +263,6 @@ class Client(object):
self._send_packet(packet.Packet(packet.EVENT, namespace=namespace, self._send_packet(packet.Packet(packet.EVENT, namespace=namespace,
data=[event] + data, id=id, data=[event] + data, id=id,
binary=binary)) 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, def send(self, data, namespace=None, callback=None, wait=False,
timeout=60): timeout=60):
@ -321,6 +295,37 @@ class Client(object):
self.emit('message', data=data, namespace=namespace, self.emit('message', data=data, namespace=namespace,
callback=callback, wait=wait, timeout=timeout) 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): def disconnect(self):
"""Disconnect from the server.""" """Disconnect from the server."""
for n in self.namespaces: for n in self.namespaces:

65
socketio/server.py

@ -4,8 +4,9 @@ import engineio
import six import six
from . import base_manager from . import base_manager
from . import packet from . import exceptions
from . import namespace from . import namespace
from . import packet
default_logger = logging.getLogger('socketio.server') default_logger = logging.getLogger('socketio.server')
@ -33,9 +34,10 @@ class Server(object):
packets. Custom json modules must have ``dumps`` and ``loads`` packets. Custom json modules must have ``dumps`` and ``loads``
functions that are compatible with the standard library functions that are compatible with the standard library
versions. versions.
:param async_handlers: If set to ``True``, event handlers are executed in :param async_handlers: If set to ``True``, event handlers for a client are
separate threads. To run handlers synchronously, executed in separate threads. To run handlers for a
set to ``False``. The default is ``False``. client synchronously, set to ``False``. The default
is ``True``.
:param kwargs: Connection parameters for the underlying Engine.IO server. :param kwargs: Connection parameters for the underlying Engine.IO server.
The Engine.IO configuration supports the following settings: The Engine.IO configuration supports the following settings:
@ -77,7 +79,7 @@ class Server(object):
``False``. The default is ``False``. ``False``. The default is ``False``.
""" """
def __init__(self, client_manager=None, logger=False, binary=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_options = kwargs
engineio_logger = engineio_options.pop('engineio_logger', None) engineio_logger = engineio_options.pop('engineio_logger', None)
if engineio_logger is not None: if engineio_logger is not None:
@ -224,11 +226,11 @@ class Server(object):
namespace = namespace or '/' namespace = namespace or '/'
self.logger.info('emitting event "%s" to %s [%s]', event, self.logger.info('emitting event "%s" to %s [%s]', event,
room or 'all', namespace) room or 'all', namespace)
self.manager.emit(event, data, namespace, room, skip_sid, callback, self.manager.emit(event, data, namespace, room=room,
**kwargs) skip_sid=skip_sid, callback=callback, **kwargs)
def send(self, data, room=None, skip_sid=None, namespace=None, 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. """Send a message to one or more connected clients.
This function emits an event with the name ``'message'``. Use 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 to always leave this parameter with its default
value of ``False``. value of ``False``.
""" """
self.emit('message', data, room, skip_sid, namespace, callback, self.emit('message', data=data, room=room, skip_sid=skip_sid,
**kwargs) 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): def enter_room(self, sid, room, namespace=None):
"""Enter a room. """Enter a room.

70
tests/asyncio/test_asyncio_client.py

@ -200,41 +200,6 @@ class TestAsyncClient(unittest.TestCase):
expected_packet.encode()) expected_packet.encode())
c._generate_ack_id.assert_called_once_with('/', 'cb') 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): def test_emit_namespace_with_callback(self):
c = asyncio_client.AsyncClient() c = asyncio_client.AsyncClient()
c._send_packet = AsyncMock() c._send_packet = AsyncMock()
@ -285,6 +250,41 @@ class TestAsyncClient(unittest.TestCase):
'message', data='data', namespace=None, callback=None, wait=False, 'message', data='data', namespace=None, callback=None, wait=False,
timeout=60) 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): def test_disconnect(self):
c = asyncio_client.AsyncClient() c = asyncio_client.AsyncClient()
c._trigger_event = AsyncMock() c._trigger_event = AsyncMock()

72
tests/asyncio/test_asyncio_server.py

@ -12,8 +12,9 @@ else:
from socketio import asyncio_server from socketio import asyncio_server
from socketio import asyncio_namespace from socketio import asyncio_namespace
from socketio import packet from socketio import exceptions
from socketio import namespace from socketio import namespace
from socketio import packet
def AsyncMock(*args, **kwargs): def AsyncMock(*args, **kwargs):
@ -83,24 +84,57 @@ class TestAsyncServer(unittest.TestCase):
def test_emit(self, eio): def test_emit(self, eio):
mgr = self._get_mock_manager() mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr) s = asyncio_server.AsyncServer(client_manager=mgr)
_run(s.emit('my event', {'foo': 'bar'}, 'room', '123', _run(s.emit('my event', {'foo': 'bar'}, room='room',
namespace='/foo', callback='cb')) skip_sid='123', namespace='/foo', callback='cb'))
s.manager.emit.mock.assert_called_once_with( 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): def test_emit_default_namespace(self, eio):
mgr = self._get_mock_manager() mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr) s = asyncio_server.AsyncServer(client_manager=mgr)
_run(s.emit('my event', {'foo': 'bar'}, 'room', '123', callback='cb')) _run(s.emit('my event', {'foo': 'bar'}, room='room',
s.manager.emit.mock.assert_called_once_with('my event', {'foo': 'bar'}, skip_sid='123', callback='cb'))
'/', 'room', '123', '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): def test_send(self, eio):
mgr = self._get_mock_manager() mgr = self._get_mock_manager()
s = asyncio_server.AsyncServer(client_manager=mgr) s = asyncio_server.AsyncServer(client_manager=mgr)
_run(s.send('foo', 'room', '123', namespace='/foo', callback='cb')) _run(s.send('foo', 'room', '123', namespace='/foo', callback='cb'))
s.manager.emit.mock.assert_called_once_with('message', 'foo', '/foo', s.manager.emit.mock.assert_called_once_with(
'room', '123', 'cb') '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): def test_enter_room(self, eio):
mgr = self._get_mock_manager() mgr = self._get_mock_manager()
@ -327,7 +361,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event(self, eio): def test_handle_event(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer() s = asyncio_server.AsyncServer(async_handlers=False)
handler = AsyncMock() handler = AsyncMock()
s.on('my message', handler) s.on('my message', handler)
_run(s._handle_eio_message('123', '2["my message","a","b","c"]')) _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): def test_handle_event_with_namespace(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer() s = asyncio_server.AsyncServer(async_handlers=False)
handler = mock.MagicMock() handler = mock.MagicMock()
s.on('my message', handler, namespace='/foo') s.on('my message', handler, namespace='/foo')
_run(s._handle_eio_message('123', '2/foo,["my message","a","b","c"]')) _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): def test_handle_event_binary(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer() s = asyncio_server.AsyncServer(async_handlers=False)
handler = mock.MagicMock() handler = mock.MagicMock()
s.on('my message', handler) s.on('my message', handler)
_run(s._handle_eio_message('123', '52-["my message","a",' _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): def test_handle_event_binary_ack(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager() 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) s.manager.initialize(s)
_run(s._handle_eio_message('123', '61-321["my message","a",' _run(s._handle_eio_message('123', '61-321["my message","a",'
'{"_placeholder":true,"num":0}]')) '{"_placeholder":true,"num":0}]'))
@ -366,7 +400,7 @@ class TestAsyncServer(unittest.TestCase):
def test_handle_event_with_ack(self, eio): def test_handle_event_with_ack(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer() s = asyncio_server.AsyncServer(async_handlers=False)
handler = mock.MagicMock(return_value='foo') handler = mock.MagicMock(return_value='foo')
s.on('my message', handler) s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","foo"]')) _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): def test_handle_event_with_ack_none(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
s = asyncio_server.AsyncServer() s = asyncio_server.AsyncServer(async_handlers=False)
handler = mock.MagicMock(return_value=None) handler = mock.MagicMock(return_value=None)
s.on('my message', handler) s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","foo"]')) _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): def test_handle_event_with_ack_tuple(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager() 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)) handler = mock.MagicMock(return_value=(1, '2', True))
s.on('my message', handler) s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","a","b","c"]')) _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): def test_handle_event_with_ack_list(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager() 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]) handler = mock.MagicMock(return_value=[1, '2', True])
s.on('my message', handler) s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","a","b","c"]')) _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): def test_handle_event_with_ack_binary(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
mgr = self._get_mock_manager() 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') handler = mock.MagicMock(return_value=b'foo')
s.on('my message', handler) s.on('my message', handler)
_run(s._handle_eio_message('123', '21000["my message","foo"]')) _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): async def on_baz(self, sid, data1, data2):
result['result'] = (data1, data2) result['result'] = (data1, data2)
s = asyncio_server.AsyncServer() s = asyncio_server.AsyncServer(async_handlers=False)
s.register_namespace(MyNamespace('/foo')) s.register_namespace(MyNamespace('/foo'))
_run(s._handle_eio_connect('123', 'environ')) _run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo')) _run(s._handle_eio_message('123', '0/foo'))

76
tests/common/test_client.py

@ -283,44 +283,6 @@ class TestClient(unittest.TestCase):
expected_packet.encode()) expected_packet.encode())
c._generate_ack_id.assert_called_once_with('/', 'cb') 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): def test_emit_namespace_with_callback(self):
c = client.Client() c = client.Client()
c._send_packet = mock.MagicMock() c._send_packet = mock.MagicMock()
@ -371,6 +333,44 @@ class TestClient(unittest.TestCase):
'message', data='data', namespace=None, callback=None, wait=False, 'message', data='data', namespace=None, callback=None, wait=False,
timeout=60) 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): def test_disconnect(self):
c = client.Client() c = client.Client()
c._trigger_event = mock.MagicMock() c._trigger_event = mock.MagicMock()

66
tests/common/test_server.py

@ -8,9 +8,10 @@ if six.PY3:
else: else:
import mock import mock
from socketio import exceptions
from socketio import namespace
from socketio import packet from socketio import packet
from socketio import server from socketio import server
from socketio import namespace
@mock.patch('engineio.Server') @mock.patch('engineio.Server')
@ -52,22 +53,55 @@ class TestServer(unittest.TestCase):
s = server.Server(client_manager=mgr) s = server.Server(client_manager=mgr)
s.emit('my event', {'foo': 'bar'}, 'room', '123', namespace='/foo', s.emit('my event', {'foo': 'bar'}, 'room', '123', namespace='/foo',
callback='cb') callback='cb')
s.manager.emit.assert_called_once_with('my event', {'foo': 'bar'}, s.manager.emit.assert_called_once_with(
'/foo', 'room', '123', 'cb') 'my event', {'foo': 'bar'}, '/foo', room='room', skip_sid='123',
callback='cb')
def test_emit_default_namespace(self, eio): def test_emit_default_namespace(self, eio):
mgr = mock.MagicMock() mgr = mock.MagicMock()
s = server.Server(client_manager=mgr) s = server.Server(client_manager=mgr)
s.emit('my event', {'foo': 'bar'}, 'room', '123', callback='cb') s.emit('my event', {'foo': 'bar'}, 'room', '123', callback='cb')
s.manager.emit.assert_called_once_with('my event', {'foo': 'bar'}, '/', s.manager.emit.assert_called_once_with(
'room', '123', 'cb') 'my event', {'foo': 'bar'}, '/', room='room', skip_sid='123',
callback='cb')
def test_send(self, eio): def test_send(self, eio):
mgr = mock.MagicMock() mgr = mock.MagicMock()
s = server.Server(client_manager=mgr) s = server.Server(client_manager=mgr)
s.send('foo', 'room', '123', namespace='/foo', callback='cb') s.send('foo', 'room', '123', namespace='/foo', callback='cb')
s.manager.emit.assert_called_once_with('message', 'foo', '/foo', s.manager.emit.assert_called_once_with(
'room', '123', 'cb') '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): def test_enter_room(self, eio):
mgr = mock.MagicMock() mgr = mock.MagicMock()
@ -265,21 +299,21 @@ class TestServer(unittest.TestCase):
s._handle_eio_disconnect('123') s._handle_eio_disconnect('123')
def test_handle_event(self, eio): def test_handle_event(self, eio):
s = server.Server() s = server.Server(async_handlers=False)
handler = mock.MagicMock() handler = mock.MagicMock()
s.on('my message', handler) s.on('my message', handler)
s._handle_eio_message('123', '2["my message","a","b","c"]') s._handle_eio_message('123', '2["my message","a","b","c"]')
handler.assert_called_once_with('123', 'a', 'b', 'c') handler.assert_called_once_with('123', 'a', 'b', 'c')
def test_handle_event_with_namespace(self, eio): def test_handle_event_with_namespace(self, eio):
s = server.Server() s = server.Server(async_handlers=False)
handler = mock.MagicMock() handler = mock.MagicMock()
s.on('my message', handler, namespace='/foo') s.on('my message', handler, namespace='/foo')
s._handle_eio_message('123', '2/foo,["my message","a","b","c"]') s._handle_eio_message('123', '2/foo,["my message","a","b","c"]')
handler.assert_called_once_with('123', 'a', 'b', 'c') handler.assert_called_once_with('123', 'a', 'b', 'c')
def test_handle_event_binary(self, eio): def test_handle_event_binary(self, eio):
s = server.Server() s = server.Server(async_handlers=False)
handler = mock.MagicMock() handler = mock.MagicMock()
s.on('my message', handler) s.on('my message', handler)
s._handle_eio_message('123', '52-["my message","a",' s._handle_eio_message('123', '52-["my message","a",'
@ -300,7 +334,7 @@ class TestServer(unittest.TestCase):
'123', '/', 321, ['my message', 'a', b'foo']) '123', '/', 321, ['my message', 'a', b'foo'])
def test_handle_event_with_ack(self, eio): def test_handle_event_with_ack(self, eio):
s = server.Server() s = server.Server(async_handlers=False)
handler = mock.MagicMock(return_value='foo') handler = mock.MagicMock(return_value='foo')
s.on('my message', handler) s.on('my message', handler)
s._handle_eio_message('123', '21000["my message","foo"]') s._handle_eio_message('123', '21000["my message","foo"]')
@ -309,7 +343,7 @@ class TestServer(unittest.TestCase):
binary=False) binary=False)
def test_handle_event_with_ack_none(self, eio): def test_handle_event_with_ack_none(self, eio):
s = server.Server() s = server.Server(async_handlers=False)
handler = mock.MagicMock(return_value=None) handler = mock.MagicMock(return_value=None)
s.on('my message', handler) s.on('my message', handler)
s._handle_eio_message('123', '21000["my message","foo"]') 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): def test_handle_event_with_ack_tuple(self, eio):
mgr = mock.MagicMock() 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)) handler = mock.MagicMock(return_value=(1, '2', True))
s.on('my message', handler) s.on('my message', handler)
s._handle_eio_message('123', '21000["my message","a","b","c"]') 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): def test_handle_event_with_ack_list(self, eio):
mgr = mock.MagicMock() 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]) handler = mock.MagicMock(return_value=[1, '2', True])
s.on('my message', handler) s.on('my message', handler)
s._handle_eio_message('123', '21000["my message","a","b","c"]') 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): def test_handle_event_with_ack_binary(self, eio):
mgr = mock.MagicMock() 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') handler = mock.MagicMock(return_value=b'foo')
s.on('my message', handler) s.on('my message', handler)
s._handle_eio_message('123', '21000["my message","foo"]') s._handle_eio_message('123', '21000["my message","foo"]')
@ -452,7 +486,7 @@ class TestServer(unittest.TestCase):
def on_baz(self, sid, data1, data2): def on_baz(self, sid, data1, data2):
result['result'] = (data1, data2) result['result'] = (data1, data2)
s = server.Server() s = server.Server(async_handlers=False)
s.register_namespace(MyNamespace('/foo')) s.register_namespace(MyNamespace('/foo'))
s._handle_eio_connect('123', 'environ') s._handle_eio_connect('123', 'environ')
s._handle_eio_message('123', '0/foo') s._handle_eio_message('123', '0/foo')

Loading…
Cancel
Save