From 558297da694f624cf5a8987a72eb2e3770dc0bca Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Sun, 4 Jan 2015 21:55:53 -0800 Subject: [PATCH] close_room() function (fixes #84) --- docs/index.rst | 1 + example/app.py | 20 +++++++++++++++- example/templates/index.html | 25 ++++++++++++++++---- flask_socketio/__init__.py | 46 ++++++++++++++++++++++++++++++++++-- test_socketio.py | 34 ++++++++++++++++++++++---- 5 files changed, 113 insertions(+), 13 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index a1dbbc9a..9e3772c4 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -311,4 +311,5 @@ API Reference .. autofunction:: send .. autofunction:: join_room .. autofunction:: leave_room +.. autofunction:: close_room .. autofunction:: disconnect diff --git a/example/app.py b/example/app.py index e7083973..f20380ba 100644 --- a/example/app.py +++ b/example/app.py @@ -4,7 +4,8 @@ import time from threading import Thread from flask import Flask, render_template, session, request -from flask.ext.socketio import SocketIO, emit, join_room, leave_room +from flask.ext.socketio import SocketIO, emit, join_room, leave_room, \ + close_room, disconnect app = Flask(__name__) app.debug = True @@ -66,6 +67,15 @@ def leave(message): 'count': session['receive_count']}) +@socketio.on('close room', namespace='/test') +def close(message): + session['receive_count'] = session.get('receive_count', 0) + 1 + emit('my response', {'data': 'Room ' + message['room'] + ' is closing.', + 'count': session['receive_count']}, + room=message['room']) + close_room(message['room']) + + @socketio.on('my room event', namespace='/test') def send_room_message(message): session['receive_count'] = session.get('receive_count', 0) + 1 @@ -74,6 +84,14 @@ def send_room_message(message): room=message['room']) +@socketio.on('disconnect request', namespace='/test') +def disconnect_request(): + session['receive_count'] = session.get('receive_count', 0) + 1 + emit('my response', + {'data': 'Disconnected!', 'count': session['receive_count']}) + disconnect() + + @socketio.on('connect', namespace='/test') def test_connect(): emit('my response', {'data': 'Connected', 'count': 0}) diff --git a/example/templates/index.html b/example/templates/index.html index 3896634f..f03d15ec 100644 --- a/example/templates/index.html +++ b/example/templates/index.html @@ -43,33 +43,48 @@ socket.emit('my room event', {room: $('#room_name').val(), data: $('#room_data').val()}); return false; }); + $('form#close').submit(function(event) { + socket.emit('close room', {room: $('#close_room').val()}); + return false; + }); + $('form#disconnect').submit(function(event) { + socket.emit('disconnect request'); + return false; + }); });

Flask-SocketIO Test

Send:

-
+
-
+
-
+
-
+
-
+
+
+ + +
+
+ +

Receive:

diff --git a/flask_socketio/__init__.py b/flask_socketio/__init__.py index 668e83eb..402c54a8 100644 --- a/flask_socketio/__init__.py +++ b/flask_socketio/__init__.py @@ -78,6 +78,9 @@ def leave_room(self, room): if self.socketio._leave_room(self, room): self.rooms.remove(room) + def close_room(self, room): + self.socketio._close_room(self, room) + def recv_connect(self): if self.socketio.server is None: self.socketio.server = self.environ['socketio'].server @@ -91,8 +94,7 @@ def recv_disconnect(self): self.socketio.server = self.environ['socketio'].server app = self.request self.socketio._dispatch_message(app, self, 'disconnect') - for room in self.rooms.copy(): - self.leave_room(room) + self.socketio._leave_all_rooms(self) return super(GenericNamespace, self).recv_disconnect() def recv_message(self, data): @@ -135,6 +137,10 @@ def send(self, message, json=False, ns_name=None, callback=None, json, callback) + def disconnect(self, silent=False): + self.socketio._leave_all_rooms(self) + return super(GenericNamespace, self).disconnect(silent) + namespaces = dict((ns_name, GenericNamespace) for ns_name in self.messages) return namespaces @@ -176,6 +182,14 @@ def _leave_room(self, namespace, room): return True return False + def _close_room(self, namespace, room): + self.close_room(room, namespace.ns_name) + + def _leave_all_rooms(self, namespace): + if namespace.ns_name in self.rooms: + for room in self.rooms[namespace.ns_name].copy(): + self._leave_room(namespace, room) + def _on_message(self, message, handler, namespace=''): if namespace not in self.messages: self.messages[namespace] = {} @@ -315,6 +329,22 @@ def send(self, message, json=False, namespace=None, room=None): if socket.active_ns.get(ns_name): socket[ns_name].base_send(message, json) + def close_room(self, room, namespace=''): + """Close a room. + + This function removes any users that are in the given room and then + deletes the room from the server. This function can be used outside + of a SocketIO event context. + + :param room: The name of the room to close. + :param namespace: The namespace under which the room exists. Defaults + to the global namespace. + """ + if namespace in self.rooms: + if room in self.rooms[namespace]: + for ns in self.rooms[namespace][room].copy(): + self._leave_room(ns, room) + def run(self, app, host=None, port=None, **kwargs): """Run the SocketIO web server. @@ -474,6 +504,18 @@ def on_leave(data): return request.namespace.leave_room(room) +def close_room(room): + """Close a room. + + This function removes any users that are in the given room and then deletes + the room from the server. This is a function that can only be called from + a SocketIO event handler. + + :param room: The name of the room to close. + """ + return request.namespace.close_room(room) + + def disconnect(silent=False): """Disconnect the client. diff --git a/test_socketio.py b/test_socketio.py index 40f497c5..f5cbfed3 100644 --- a/test_socketio.py +++ b/test_socketio.py @@ -15,84 +15,103 @@ socketio = SocketIO(app) disconnected = None + @socketio.on('connect') def on_connect(): send('connected') session['a'] = 'b' + @socketio.on('disconnect') -def on_connect(): +def on_disconnect(): global disconnected disconnected = '/' + @socketio.on('connect', namespace='/test') def on_connect_test(): send('connected-test') + @socketio.on('disconnect', namespace='/test') def on_disconnect_test(): global disconnected disconnected = '/test' + @socketio.on('message') def on_message(message): send(message) + @socketio.on('json') def on_json(data): send(data, json=True, broadcast=True) + @socketio.on('message', namespace='/test') def on_message_test(message): send(message) + @socketio.on('json', namespace='/test') def on_json_test(data): send(data, json=True, namespace='/test') + @socketio.on('my custom event') def on_custom_event(data): emit('my custom response', data) + @socketio.on('my custom namespace event', namespace='/test') def on_custom_event_test(data): emit('my custom namespace response', data, namespace='/test') + @socketio.on('my custom broadcast event') def on_custom_event_broadcast(data): emit('my custom response', data, broadcast=True) + @socketio.on('my custom broadcast namespace event', namespace='/test') def on_custom_event_broadcast_test(data): emit('my custom namespace response', data, namespace='/test', broadcast=True) + @socketio.on('join room') def on_join_room(data): join_room(data['room']) + @socketio.on('leave room') def on_leave_room(data): leave_room(data['room']) + @socketio.on('join room', namespace='/test') -def on_join_room(data): +def on_join_room_namespace(data): join_room(data['room']) + @socketio.on('leave room', namespace='/test') -def on_leave_room(data): +def on_leave_room_namespace(data): leave_room(data['room']) + @socketio.on('my room event') def on_room_event(data): room = data.pop('room') emit('my room response', data, room=room) + @socketio.on('my room namespace event', namespace='/test') def on_room_namespace_event(data): room = data.pop('room') send('room message', room=room) + @socketio.on_error() def error_handler(value): if isinstance(value, AssertionError): @@ -101,10 +120,12 @@ def error_handler(value): else: raise value + @socketio.on('error testing') def raise_error(data): raise AssertionError() + @socketio.on_error('/test') def error_handler_namespace(value): if isinstance(value, AssertionError): @@ -113,17 +134,20 @@ def error_handler_namespace(value): else: raise value + @socketio.on("error testing", namespace='/test') def raise_error_namespace(data): raise AssertionError() + @socketio.on_error_default def error_handler_default(value): if isinstance(value, AssertionError): global error_testing_default error_testing_default = True else: - raise exception, value, traceback + raise value + @socketio.on("error testing", namespace='/unused_namespace') def raise_error_default(data): @@ -306,7 +330,7 @@ def test_room(self): self.assertTrue(received[0]['name'] == 'message') self.assertTrue(received[0]['args'] == 'room message') self.assertTrue(len(socketio.rooms) == 1) - client3.disconnect('/test') + socketio.close_room('one', namespace='/test') self.assertTrue(len(socketio.rooms) == 0) def test_error_handling(self):