Browse Source

Added class-based namespaces.

pull/43/merge^2
Robert Schindler 9 years ago
parent
commit
fe40b30e35
  1. 5
      socketio/__init__.py
  2. 56
      socketio/namespace.py
  3. 14
      socketio/server.py
  4. 11
      tests/test_server.py

5
socketio/__init__.py

@ -1,9 +1,10 @@
from .middleware import Middleware
from .namespace import Namespace
from .base_manager import BaseManager
from .pubsub_manager import PubSubManager
from .kombu_manager import KombuManager
from .redis_manager import RedisManager
from .server import Server
__all__ = [Middleware, Server, BaseManager, PubSubManager, KombuManager,
RedisManager]
__all__ = [Middleware, Namespace, Server, BaseManager, PubSubManager,
KombuManager, RedisManager]

56
socketio/namespace.py

@ -0,0 +1,56 @@
import types
class Namespace(object):
"""A container for a set of event handlers for a specific namespace.
A method of this class named ``on_xxx`` is considered as the event handler
for the event ``'xxx'`` in the namespace this class is registered to.
There are also the following methods available that insert the current
namespace automatically when none is given before they call their matching
method of the ``Server`` instance:
``emit``, ``send``, ``enter_room``, ``leave_room``, ``close_room``,
``rooms``, ``disconnect``
Example:
from socketio import Namespace, Server
class ChatNamespace(Namespace):
def on_msg(self, sid, msg):
# self.server references to the socketio.Server object
data = "[%s]: %s" \
% (self.server.environ[sid].get("REMOTE_ADDR"), msg)
# Note that we don't pass namespace="/chat" to the emit method.
# It is done automatically for us.
self.emit("msg", data, skip_sid=sid)
return "received your message: %s" % msg
sio = socketio.Server()
sio.register_namespace("/chat", ChatNamespace)
"""
def __init__(self, name, server):
self.name = name
self.server = server
# wrap methods of Server object
def get_wrapped_method(func_name):
def wrapped_func(self, *args, **kwargs):
"""If namespace is None, it is automatically set to this
object's one before the original method is called.
"""
if kwargs.get('namespace') is None:
kwargs['namespace'] = self.name
return getattr(self.server, func_name)(*args, **kwargs)
return types.MethodType(wrapped_func, self)
for func_name in ('emit', 'send', 'enter_room', 'leave_room',
'close_room', 'rooms', 'disconnect'):
setattr(self, func_name, get_wrapped_method(func_name))
def _get_handlers(self):
"""Returns a dict of event names and handlers this namespace provides."""
handlers = {}
for attr_name in dir(self):
if attr_name.startswith('on_'):
handlers[attr_name[3:]] = getattr(self, attr_name)
return handlers

14
socketio/server.py

@ -150,6 +150,20 @@ class Server(object):
return set_handler
set_handler(handler)
def register_namespace(self, name, namespace_class):
"""Register all handlers of the given ``namespace_class`` under the
namespace named by ``name``.
:param name: The namespace's name. It can be any string.
:param namespace_class: The sub class of ``Namespace`` to register
handlers of. Don't pass an instance instead.
See documentation of ``Namespace`` class for an example.
"""
namespace = namespace_class(name, self)
for event, handler in six.iteritems(namespace._get_handlers()):
self.on(event, handler=handler, namespace=name)
def emit(self, event, data=None, room=None, skip_sid=None, namespace=None,
callback=None):
"""Emit a custom event to one or more connected clients.

11
tests/test_server.py

@ -8,6 +8,7 @@ if six.PY3:
else:
import mock
from socketio import namespace
from socketio import packet
from socketio import server
@ -45,6 +46,16 @@ class TestServer(unittest.TestCase):
self.assertEqual(s.handlers['/']['disconnect'], bar)
self.assertEqual(s.handlers['/foo']['disconnect'], bar)
def test_register_namespace(self, eio):
class NS(namespace.Namespace):
def on_foo(self):
self.emit("bar")
s = server.Server()
s.register_namespace('/ns', NS)
self.assertIsNotNone(s.handlers['/ns'].get('foo'))
def test_on_bad_event_name(self, eio):
s = server.Server()
self.assertRaises(ValueError, s.on, 'two-words')

Loading…
Cancel
Save