Browse Source

Support catch-all namespaces (Fixes #1288)

pull/1298/head
mooomooo 1 year ago
committed by Miguel Grinberg
parent
commit
801241378e
Failed to extract signature
  1. 34
      docs/client.rst
  2. 29
      docs/server.rst
  3. 15
      src/socketio/async_client.py
  4. 18
      src/socketio/async_server.py
  5. 40
      src/socketio/base_client.py
  6. 42
      src/socketio/base_server.py
  7. 15
      src/socketio/client.py
  8. 18
      src/socketio/server.py
  9. 32
      tests/async/test_client.py
  10. 63
      tests/async/test_server.py
  11. 58
      tests/common/test_client.py
  12. 61
      tests/common/test_server.py

34
docs/client.rst

@ -253,25 +253,49 @@ or can also be coroutines::
If the server includes arguments with an event, those are passed to the If the server includes arguments with an event, those are passed to the
handler function as arguments. handler function as arguments.
Catch-All Event Handlers Catch-All Event and Namespace Handlers
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A "catch-all" event handler is invoked for any events that do not have an A "catch-all" event handler is invoked for any events that do not have an
event handler. You can define a catch-all handler using ``'*'`` as event name:: event handler. You can define a catch-all handler using ``'*'`` as event name::
@sio.on('*') @sio.on('*')
def catch_all(event, data): def any_event(event, sid, data):
pass pass
Asyncio clients can also use a coroutine:: Asyncio servers can also use a coroutine::
@sio.on('*') @sio.on('*')
async def catch_all(event, data): async def any_event(event, sid, data):
pass pass
A catch-all event handler receives the event name as a first argument. The A catch-all event handler receives the event name as a first argument. The
remaining arguments are the same as for a regular event handler. remaining arguments are the same as for a regular event handler.
The ``connect`` and ``disconnect`` events have to be defined explicitly and are
not invoked on a catch-all event handler.
Similarily, a "catch-all" namespace handler is invoked for any connected
namespaces that do not have an explicitly defined event handler. As with
catch-all events, ``'*'`` is used in place of a namespace::
@sio.on('my_event', namespace='*')
def my_event_any_namespace(namespace, sid, data):
pass
For these events, the namespace is passed as first argument, followed by the
regular arguments of the event.
Lastly, it is also possible to define a "catch-all" handler for all events on
all namespaces::
@sio.on('*', namespace='*')
def any_event_any_namespace(event, namespace, sid, data):
pass
Event handlers with catch-all events and namespaces receive the event name and
the namespace as first and second arguments.
Connect, Connect Error and Disconnect Event Handlers Connect, Connect Error and Disconnect Event Handlers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

29
docs/server.rst

@ -178,20 +178,20 @@ The ``sid`` argument is the Socket.IO session id, a unique identifier of each
client connection. All the events sent by a given client will have the same client connection. All the events sent by a given client will have the same
``sid`` value. ``sid`` value.
Catch-All Event Handlers Catch-All Event and Namespace Handlers
------------------------ --------------------------------------
A "catch-all" event handler is invoked for any events that do not have an A "catch-all" event handler is invoked for any events that do not have an
event handler. You can define a catch-all handler using ``'*'`` as event name:: event handler. You can define a catch-all handler using ``'*'`` as event name::
@sio.on('*') @sio.on('*')
def catch_all(event, sid, data): def any_event(event, sid, data):
pass pass
Asyncio servers can also use a coroutine:: Asyncio servers can also use a coroutine::
@sio.on('*') @sio.on('*')
async def catch_all(event, sid, data): async def any_event(event, sid, data):
pass pass
A catch-all event handler receives the event name as a first argument. The A catch-all event handler receives the event name as a first argument. The
@ -200,6 +200,27 @@ remaining arguments are the same as for a regular event handler.
The ``connect`` and ``disconnect`` events have to be defined explicitly and are The ``connect`` and ``disconnect`` events have to be defined explicitly and are
not invoked on a catch-all event handler. not invoked on a catch-all event handler.
Similarily, a "catch-all" namespace handler is invoked for any connected
namespaces that do not have an explicitly defined event handler. As with
catch-all events, ``'*'`` is used in place of a namespace::
@sio.on('my_event', namespace='*')
def my_event_any_namespace(namespace, sid, data):
pass
For these events, the namespace is passed as first argument, followed by the
regular arguments of the event.
Lastly, it is also possible to define a "catch-all" handler for all events on
all namespaces::
@sio.on('*', namespace='*')
def any_event_any_namespace(event, namespace, sid, data):
pass
Event handlers with catch-all events and namespaces receive the event name and
the namespace as first and second arguments.
Connect and Disconnect Event Handlers Connect and Disconnect Event Handlers
------------------------------------- -------------------------------------

15
src/socketio/async_client.py

@ -429,14 +429,7 @@ class AsyncClient(base_client.BaseClient):
async def _trigger_event(self, event, namespace, *args): async def _trigger_event(self, event, namespace, *args):
"""Invoke an application event handler.""" """Invoke an application event handler."""
# first see if we have an explicit handler for the event # first see if we have an explicit handler for the event
if namespace in self.handlers: handler, args = self._get_event_handler(event, namespace, args)
handler = None
if event in self.handlers[namespace]:
handler = self.handlers[namespace][event]
elif event not in self.reserved_events and \
'*' in self.handlers[namespace]:
handler = self.handlers[namespace]['*']
args = (event, *args)
if handler: if handler:
if asyncio.iscoroutinefunction(handler): if asyncio.iscoroutinefunction(handler):
try: try:
@ -448,9 +441,9 @@ class AsyncClient(base_client.BaseClient):
return ret return ret
# or else, forward the event to a namepsace handler if one exists # or else, forward the event to a namepsace handler if one exists
elif namespace in self.namespace_handlers: handler, args = self._get_namespace_handler(namespace, args)
return await self.namespace_handlers[namespace].trigger_event( if handler:
event, *args) return await handler.trigger_event(event, *args)
async def _handle_reconnect(self): async def _handle_reconnect(self):
if self._reconnect_abort is None: # pragma: no cover if self._reconnect_abort is None: # pragma: no cover

18
src/socketio/async_server.py

@ -617,14 +617,7 @@ class AsyncServer(base_server.BaseServer):
async def _trigger_event(self, event, namespace, *args): async def _trigger_event(self, event, namespace, *args):
"""Invoke an application event handler.""" """Invoke an application event handler."""
# first see if we have an explicit handler for the event # first see if we have an explicit handler for the event
if namespace in self.handlers: handler, args = self._get_event_handler(event, namespace, args)
handler = None
if event in self.handlers[namespace]:
handler = self.handlers[namespace][event]
elif event not in self.reserved_events and \
'*' in self.handlers[namespace]:
handler = self.handlers[namespace]['*']
args = (event, *args)
if handler: if handler:
if asyncio.iscoroutinefunction(handler): if asyncio.iscoroutinefunction(handler):
try: try:
@ -634,14 +627,13 @@ class AsyncServer(base_server.BaseServer):
else: else:
ret = handler(*args) ret = handler(*args)
return ret return ret
# or else, forward the event to a namespace handler if one exists
handler, args = self._get_namespace_handler(namespace, args)
if handler:
return await handler.trigger_event(event, *args)
else: else:
return self.not_handled return self.not_handled
# or else, forward the event to a namepsace handler if one exists
elif namespace in self.namespace_handlers: # pragma: no branch
return await self.namespace_handlers[namespace].trigger_event(
event, *args)
async def _handle_eio_connect(self, eio_sid, environ): async def _handle_eio_connect(self, eio_sid, environ):
"""Handle the Engine.IO connection event.""" """Handle the Engine.IO connection event."""
if not self.manager_initialized: if not self.manager_initialized:

40
src/socketio/base_client.py

@ -219,6 +219,46 @@ class BaseClient:
""" """
return self.eio.transport() return self.eio.transport()
def _get_event_handler(self, event, namespace, args):
# return the appropriate application event handler
#
# Resolution priority:
# - self.handlers[namespace][event]
# - self.handlers[namespace]["*"]
# - self.handlers["*"][event]
# - self.handlers["*"]["*"]
handler = None
if namespace in self.handlers:
if event in self.handlers[namespace]:
handler = self.handlers[namespace][event]
elif event not in self.reserved_events and \
'*' in self.handlers[namespace]:
handler = self.handlers[namespace]['*']
args = (event, *args)
elif '*' in self.handlers:
if event in self.handlers['*']:
handler = self.handlers['*'][event]
args = (namespace, *args)
elif event not in self.reserved_events and \
'*' in self.handlers['*']:
handler = self.handlers['*']['*']
args = (event, namespace, *args)
return handler, args
def _get_namespace_handler(self, namespace, args):
# Return the appropriate application event handler.
#
# Resolution priority:
# - self.namespace_handlers[namespace]
# - self.namespace_handlers["*"]
handler = None
if namespace in self.namespace_handlers:
handler = self.namespace_handlers[namespace]
elif '*' in self.namespace_handlers:
handler = self.namespace_handlers['*']
args = (namespace, *args)
return handler, args
def _generate_ack_id(self, namespace, callback): def _generate_ack_id(self, namespace, callback):
"""Generate a unique identifier for an ACK packet.""" """Generate a unique identifier for an ACK packet."""
namespace = namespace or '/' namespace = namespace or '/'

42
src/socketio/base_server.py

@ -196,6 +196,48 @@ class BaseServer:
eio_sid = self.manager.eio_sid_from_sid(sid, namespace or '/') eio_sid = self.manager.eio_sid_from_sid(sid, namespace or '/')
return self.environ.get(eio_sid) return self.environ.get(eio_sid)
def _get_event_handler(self, event, namespace, args):
# Return the appropriate application event handler
#
# Resolution priority:
# - self.handlers[namespace][event]
# - self.handlers[namespace]["*"]
# - self.handlers["*"][event]
# - self.handlers["*"]["*"]
handler = None
print(event, namespace)
print(namespace in self.handlers)
if namespace in self.handlers:
if event in self.handlers[namespace]:
handler = self.handlers[namespace][event]
elif event not in self.reserved_events and \
'*' in self.handlers[namespace]:
handler = self.handlers[namespace]['*']
args = (event, *args)
elif '*' in self.handlers:
if event in self.handlers['*']:
handler = self.handlers['*'][event]
args = (namespace, *args)
elif event not in self.reserved_events and \
'*' in self.handlers['*']:
handler = self.handlers['*']['*']
args = (event, namespace, *args)
return handler, args
def _get_namespace_handler(self, namespace, args):
# Return the appropriate application event handler.
#
# Resolution priority:
# - self.namespace_handlers[namespace]
# - self.namespace_handlers["*"]
handler = None
if namespace in self.namespace_handlers:
handler = self.namespace_handlers[namespace]
elif '*' in self.namespace_handlers:
handler = self.namespace_handlers['*']
args = (namespace, *args)
return handler, args
def _handle_eio_connect(self): # pragma: no cover def _handle_eio_connect(self): # pragma: no cover
raise NotImplementedError() raise NotImplementedError()

15
src/socketio/client.py

@ -404,17 +404,14 @@ class Client(base_client.BaseClient):
def _trigger_event(self, event, namespace, *args): def _trigger_event(self, event, namespace, *args):
"""Invoke an application event handler.""" """Invoke an application event handler."""
# first see if we have an explicit handler for the event # first see if we have an explicit handler for the event
if namespace in self.handlers: handler, args = self._get_event_handler(event, namespace, args)
if event in self.handlers[namespace]: if handler:
return self.handlers[namespace][event](*args) return handler(*args)
elif event not in self.reserved_events and \
'*' in self.handlers[namespace]:
return self.handlers[namespace]['*'](event, *args)
# or else, forward the event to a namespace handler if one exists # or else, forward the event to a namespace handler if one exists
elif namespace in self.namespace_handlers: handler, args = self._get_namespace_handler(namespace, args)
return self.namespace_handlers[namespace].trigger_event( if handler:
event, *args) return handler.trigger_event(event, *args)
def _handle_reconnect(self): def _handle_reconnect(self):
if self._reconnect_abort is None: # pragma: no cover if self._reconnect_abort is None: # pragma: no cover

18
src/socketio/server.py

@ -604,20 +604,16 @@ class Server(base_server.BaseServer):
def _trigger_event(self, event, namespace, *args): def _trigger_event(self, event, namespace, *args):
"""Invoke an application event handler.""" """Invoke an application event handler."""
# first see if we have an explicit handler for the event # first see if we have an explicit handler for the event
if namespace in self.handlers: handler, args = self._get_event_handler(event, namespace, args)
if event in self.handlers[namespace]: if handler:
return self.handlers[namespace][event](*args) return handler(*args)
elif event not in self.reserved_events and \ # or else, forward the event to a namespace handler if one exists
'*' in self.handlers[namespace]: handler, args = self._get_namespace_handler(namespace, args)
return self.handlers[namespace]['*'](event, *args) if handler:
return handler.trigger_event(event, *args)
else: else:
return self.not_handled return self.not_handled
# or else, forward the event to a namespace handler if one exists
elif namespace in self.namespace_handlers: # pragma: no branch
return self.namespace_handlers[namespace].trigger_event(
event, *args)
def _handle_eio_connect(self, eio_sid, environ): def _handle_eio_connect(self, eio_sid, environ):
"""Handle the Engine.IO connection event.""" """Handle the Engine.IO connection event."""
if not self.manager_initialized: if not self.manager_initialized:

32
tests/async/test_client.py

@ -857,6 +857,38 @@ class TestAsyncClient(unittest.TestCase):
_run(c._trigger_event('foo', '/', 1, '2')) _run(c._trigger_event('foo', '/', 1, '2'))
assert result == [1, '2'] assert result == [1, '2']
def test_trigger_event_with_catchall_class_namespace(self):
result = {}
class MyNamespace(async_namespace.AsyncClientNamespace):
def on_connect(self, ns):
result['result'] = (ns,)
def on_disconnect(self, ns):
result['result'] = ('disconnect', ns)
def on_foo(self, ns, data):
result['result'] = (ns, data)
def on_bar(self, ns):
result['result'] = 'bar' + ns
def on_baz(self, ns, data1, data2):
result['result'] = (ns, data1, data2)
c = async_client.AsyncClient()
c.register_namespace(MyNamespace('*'))
_run(c._trigger_event('connect', '/foo'))
assert result['result'] == ('/foo',)
_run(c._trigger_event('foo', '/foo', 'a'))
assert result['result'] == ('/foo', 'a')
_run(c._trigger_event('bar', '/foo'))
assert result['result'] == 'bar/foo'
_run(c._trigger_event('baz', '/foo', 'a', 'b'))
assert result['result'] == ('/foo', 'a', 'b')
_run(c._trigger_event('disconnect', '/foo'))
assert result['result'] == ('disconnect', '/foo')
def test_trigger_event_unknown_namespace(self): def test_trigger_event_unknown_namespace(self):
c = async_client.AsyncClient() c = async_client.AsyncClient()
result = [] result = []

63
tests/async/test_server.py

@ -621,6 +621,35 @@ class TestAsyncServer(unittest.TestCase):
catchall_handler.assert_called_once_with( catchall_handler.assert_called_once_with(
'my message', sid, 'a', 'b', 'c') 'my message', sid, 'a', 'b', 'c')
def test_handle_event_with_catchall_namespace(self, eio):
eio.return_value.send = AsyncMock()
s = async_server.AsyncServer(async_handlers=False)
sid_foo = _run(s.manager.connect('123', '/foo'))
sid_bar = _run(s.manager.connect('123', '/bar'))
connect_star_handler = mock.MagicMock()
msg_foo_handler = mock.MagicMock()
msg_star_handler = mock.MagicMock()
star_foo_handler = mock.MagicMock()
star_star_handler = mock.MagicMock()
s.on('connect', connect_star_handler, namespace='*')
s.on('msg', msg_foo_handler, namespace='/foo')
s.on('msg', msg_star_handler, namespace='*')
s.on('*', star_foo_handler, namespace='/foo')
s.on('*', star_star_handler, namespace='*')
_run(s._trigger_event('connect', '/bar', sid_bar))
_run(s._handle_eio_message('123', '2/foo,["msg","a","b"]'))
_run(s._handle_eio_message('123', '2/bar,["msg","a","b"]'))
_run(s._handle_eio_message('123', '2/foo,["my message","a","b","c"]'))
_run(s._handle_eio_message('123', '2/bar,["my message","a","b","c"]'))
_run(s._trigger_event('disconnect', '/bar', sid_bar))
connect_star_handler.assert_called_once_with('/bar', sid_bar)
msg_foo_handler.assert_called_once_with(sid_foo, 'a', 'b')
msg_star_handler.assert_called_once_with('/bar', sid_bar, 'a', 'b')
star_foo_handler.assert_called_once_with(
'my message', sid_foo, 'a', 'b', 'c')
star_star_handler.assert_called_once_with(
'my message', '/bar', sid_bar, 'a', 'b', 'c')
def test_handle_event_with_disconnected_namespace(self, eio): def test_handle_event_with_disconnected_namespace(self, eio):
eio.return_value.send = AsyncMock() eio.return_value.send = AsyncMock()
s = async_server.AsyncServer(async_handlers=False) s = async_server.AsyncServer(async_handlers=False)
@ -904,6 +933,40 @@ class TestAsyncServer(unittest.TestCase):
_run(s.disconnect('1', '/foo')) _run(s.disconnect('1', '/foo'))
assert result['result'] == ('disconnect', '1') assert result['result'] == ('disconnect', '1')
def test_catchall_namespace_handler(self, eio):
eio.return_value.send = AsyncMock()
result = {}
class MyNamespace(async_namespace.AsyncNamespace):
def on_connect(self, ns, sid, environ):
result['result'] = (sid, ns, environ)
async def on_disconnect(self, ns, sid):
result['result'] = ('disconnect', sid, ns)
async def on_foo(self, ns, sid, data):
result['result'] = (sid, ns, data)
def on_bar(self, ns, sid):
result['result'] = 'bar' + ns
async def on_baz(self, ns, sid, data1, data2):
result['result'] = (ns, data1, data2)
s = async_server.AsyncServer(async_handlers=False, namespaces='*')
s.register_namespace(MyNamespace('*'))
_run(s._handle_eio_connect('123', 'environ'))
_run(s._handle_eio_message('123', '0/foo,'))
assert result['result'] == ('1', '/foo', 'environ')
_run(s._handle_eio_message('123', '2/foo,["foo","a"]'))
assert result['result'] == ('1', '/foo', 'a')
_run(s._handle_eio_message('123', '2/foo,["bar"]'))
assert result['result'] == 'bar/foo'
_run(s._handle_eio_message('123', '2/foo,["baz","a","b"]'))
assert result['result'] == ('/foo', 'a', 'b')
_run(s.disconnect('1', '/foo'))
assert result['result'] == ('disconnect', '1', '/foo')
def test_bad_namespace_handler(self, eio): def test_bad_namespace_handler(self, eio):
class Dummy(object): class Dummy(object):
pass pass

58
tests/common/test_client.py

@ -970,6 +970,64 @@ class TestClient(unittest.TestCase):
handler.assert_called_once_with(1, '2') handler.assert_called_once_with(1, '2')
catchall_handler.assert_called_once_with('bar', 1, '2', 3) catchall_handler.assert_called_once_with('bar', 1, '2', 3)
def test_trigger_event_with_catchall_namespace(self):
c = client.Client()
connect_star_handler = mock.MagicMock()
msg_foo_handler = mock.MagicMock()
msg_star_handler = mock.MagicMock()
star_foo_handler = mock.MagicMock()
star_star_handler = mock.MagicMock()
c.on('connect', connect_star_handler, namespace='*')
c.on('msg', msg_foo_handler, namespace='/foo')
c.on('msg', msg_star_handler, namespace='*')
c.on('*', star_foo_handler, namespace='/foo')
c.on('*', star_star_handler, namespace='*')
c._trigger_event('connect', '/bar')
c._trigger_event('msg', '/foo', 'a', 'b')
c._trigger_event('msg', '/bar', 'a', 'b')
c._trigger_event('my message', '/foo', 'a', 'b', 'c')
c._trigger_event('my message', '/bar', 'a', 'b', 'c')
c._trigger_event('disconnect', '/bar')
connect_star_handler.assert_called_once_with('/bar')
msg_foo_handler.assert_called_once_with('a', 'b')
msg_star_handler.assert_called_once_with('/bar', 'a', 'b')
star_foo_handler.assert_called_once_with(
'my message', 'a', 'b', 'c')
star_star_handler.assert_called_once_with(
'my message', '/bar', 'a', 'b', 'c')
def test_trigger_event_with_catchall_namespace_handler(self):
result = {}
class MyNamespace(namespace.ClientNamespace):
def on_connect(self, ns):
result['result'] = (ns,)
def on_disconnect(self, ns):
result['result'] = ('disconnect', ns)
def on_foo(self, ns, data):
result['result'] = (ns, data)
def on_bar(self, ns):
result['result'] = 'bar' + ns
def on_baz(self, ns, data1, data2):
result['result'] = (ns, data1, data2)
c = client.Client()
c.register_namespace(MyNamespace('*'))
c._trigger_event('connect', '/foo')
assert result['result'] == ('/foo',)
c._trigger_event('foo', '/foo', 'a')
assert result['result'] == ('/foo', 'a')
c._trigger_event('bar', '/foo')
assert result['result'] == 'bar/foo'
c._trigger_event('baz', '/foo', 'a', 'b')
assert result['result'] == ('/foo', 'a', 'b')
c._trigger_event('disconnect', '/foo')
assert result['result'] == ('disconnect', '/foo')
def test_trigger_event_class_namespace(self): def test_trigger_event_class_namespace(self):
c = client.Client() c = client.Client()
result = [] result = []

61
tests/common/test_server.py

@ -574,6 +574,34 @@ class TestServer(unittest.TestCase):
catchall_handler.assert_called_once_with( catchall_handler.assert_called_once_with(
'my message', '1', 'a', 'b', 'c') 'my message', '1', 'a', 'b', 'c')
def test_handle_event_with_catchall_namespace(self, eio):
s = server.Server(async_handlers=False)
sid_foo = s.manager.connect('123', '/foo')
sid_bar = s.manager.connect('123', '/bar')
connect_star_handler = mock.MagicMock()
msg_foo_handler = mock.MagicMock()
msg_star_handler = mock.MagicMock()
star_foo_handler = mock.MagicMock()
star_star_handler = mock.MagicMock()
s.on('connect', connect_star_handler, namespace='*')
s.on('msg', msg_foo_handler, namespace='/foo')
s.on('msg', msg_star_handler, namespace='*')
s.on('*', star_foo_handler, namespace='/foo')
s.on('*', star_star_handler, namespace='*')
s._trigger_event('connect', '/bar', sid_bar)
s._handle_eio_message('123', '2/foo,["msg","a","b"]')
s._handle_eio_message('123', '2/bar,["msg","a","b"]')
s._handle_eio_message('123', '2/foo,["my message","a","b","c"]')
s._handle_eio_message('123', '2/bar,["my message","a","b","c"]')
s._trigger_event('disconnect', '/bar', sid_bar)
connect_star_handler.assert_called_once_with('/bar', sid_bar)
msg_foo_handler.assert_called_once_with(sid_foo, 'a', 'b')
msg_star_handler.assert_called_once_with('/bar', sid_bar, 'a', 'b')
star_foo_handler.assert_called_once_with(
'my message', sid_foo, 'a', 'b', 'c')
star_star_handler.assert_called_once_with(
'my message', '/bar', sid_bar, 'a', 'b', 'c')
def test_handle_event_with_disconnected_namespace(self, eio): def test_handle_event_with_disconnected_namespace(self, eio):
s = server.Server(async_handlers=False) s = server.Server(async_handlers=False)
s.manager.connect('123', '/foo') s.manager.connect('123', '/foo')
@ -815,6 +843,39 @@ class TestServer(unittest.TestCase):
s.disconnect('1', '/foo') s.disconnect('1', '/foo')
assert result['result'] == ('disconnect', '1') assert result['result'] == ('disconnect', '1')
def test_catchall_namespace_handler(self, eio):
result = {}
class MyNamespace(namespace.Namespace):
def on_connect(self, ns, sid, environ):
result['result'] = (sid, ns, environ)
def on_disconnect(self, ns, sid):
result['result'] = ('disconnect', sid, ns)
def on_foo(self, ns, sid, data):
result['result'] = (sid, ns, data)
def on_bar(self, ns, sid):
result['result'] = 'bar' + ns
def on_baz(self, ns, sid, data1, data2):
result['result'] = (ns, data1, data2)
s = server.Server(async_handlers=False, namespaces='*')
s.register_namespace(MyNamespace('*'))
s._handle_eio_connect('123', 'environ')
s._handle_eio_message('123', '0/foo,')
assert result['result'] == ('1', '/foo', 'environ')
s._handle_eio_message('123', '2/foo,["foo","a"]')
assert result['result'] == ('1', '/foo', 'a')
s._handle_eio_message('123', '2/foo,["bar"]')
assert result['result'] == 'bar/foo'
s._handle_eio_message('123', '2/foo,["baz","a","b"]')
assert result['result'] == ('/foo', 'a', 'b')
s.disconnect('1', '/foo')
assert result['result'] == ('disconnect', '1', '/foo')
def test_bad_namespace_handler(self, eio): def test_bad_namespace_handler(self, eio):
class Dummy(object): class Dummy(object):
pass pass

Loading…
Cancel
Save