diff --git a/.travis.yml b/.travis.yml index a77c7635..6127a37d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: python python: - "2.7" - - "2.6" + - "3.3" + - "3.4" + - "pypy" install: python setup.py install script: python setup.py test diff --git a/docs/index.rst b/docs/index.rst index 998936f3..19cb5550 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,18 +29,56 @@ connection to the server. Versions 1.3.5 or newer of the Socket.IO client are recommended. Versions of the Socket.IO client prior to 1.0 are not supported anymore. -Note that older versions of Flask-SocketIO had a completely different set of -requirements. These versions had a dependency on -`gevent-socketio `_ and -`gevent-websocket `_, which are -not used anymore. - Current Limitations ~~~~~~~~~~~~~~~~~~~ - Flask-SocketIO can only run in a single worker process at this time. Work is currently in progress to eliminate this limitation. +Differences With Flask-SocketIO Versions 0.x +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Older versions of Flask-SocketIO had a completely different set of +requirements. These versions had a dependency on +`gevent-socketio `_ and +`gevent-websocket `_, which +were dropped in release 1.0. + +In spite of the change in dependencies, there aren't many significant +changes introduced in version 1.0. Below is a detailed list of +the actual differences: + +- Release 1.0 drops support for Python 2.6, and adds support for Python 3.3, + Python 3.4, and pypy. +- Releases 0.x required an old version of the Socket.IO Javascript client. + Starting with release 1.0, the current releases of Socket.IO and Engine.IO + are supported. +- The 0.x releases depended on gevent, gevent-socketio and gevent-websocket. + In release 1.0 gevent-socketio and gevent-websocket are not used anymore, + and gevent is one of three options for backend web server, with eventlet + and any regular multi-threaded WSGI server, including Flask's development + web server. +- The Socket.IO server options have changed in release 1.0. They can be + provided in the SocketIO constructor, or in the ``run()`` call. The options + provided in these two are merged before they are used. +- The 0.x releases exposed the gevent-socketio connection as + ``request.namespace``. In release 1.0 this is not available anymore. The + request object defines ``request.namespace`` as the name of the namespace + being handled, and adds ``request.sid``, defined as the unique session ID + for the client connection, and ``request.event``, which contains the event + name and arguments. +- To get the list of rooms a client was in the 0.x release required the + application to use a private structure of gevent-socketio, with the + expression ``request.namespace.rooms``. This is not available in release + 1.0, which includes a proper ``rooms()`` function. +- The recommended "trick" to send a message to an individual client was to + put each client in a separate room, then address messages to the desired + room. This was formalized in release 1.0, where clients are assigned a room + automatically when they connect. +- The ``'connect'`` event for the global namespace did not fire on releases + prior to 1.0. This has been fixed and now this event fires as expected. +- Support for client-side callbacks was introduced in release 1.0. + Initialization -------------- @@ -168,10 +206,20 @@ received by the client:: emit('my response', json, callback=ack) When using callbacks the Javascript client receives a callback function to -invoke upon receipt of the message. When the client calls the callback -function the server invokes the corresponding server-side callback. The client -can pass arguments in the callback function, which are transferred to the -server and given to the server-side callback as function arguments. +invoke upon receipt of the message. After the client application invokes the +callback function the server invokes the corresponding server-side callback. +If the client-side callback returns any values, these are provided as +arguments to the server-side callback. + +The client application can also request an acknoledgement callback for an +event sent to the server. If the server wants to provide arguments for this +callback, it must return them from the event handler function:: + + @socketio.on('my event') + def handle_my_custom_event(json): + # ... handle the event + + return 'foo', 'bar', 123 # client callback will receive these 3 arguments Broadcasting ------------ diff --git a/flask_socketio/__init__.py b/flask_socketio/__init__.py index 43e5c333..f029dad2 100755 --- a/flask_socketio/__init__.py +++ b/flask_socketio/__init__.py @@ -30,7 +30,7 @@ def init_app(self, app, **kwargs): raise RuntimeError('Cannot associate a SocketIO instance with ' 'more than one application') if not hasattr(app, 'extensions'): - app.extensions = {} + app.extensions = {} # pragma: no cover app.extensions['socketio'] = self self.server_options = kwargs self.app = app @@ -232,6 +232,7 @@ def run(self, app=None, host=None, port=None, **kwargs): self.app = app self.server_options.update(kwargs) + test_mode = self.server_options.pop('test_mode', False) log_output = self.server_options.pop('log_output', app.debug) use_reloader = self.server_options.pop('use_reloader', app.debug) resource = self.server_options.pop('resource', 'socket.io') @@ -248,22 +249,25 @@ def run(self, app=None, host=None, port=None, **kwargs): app.wsgi_app = socketio.Middleware(self.server, app.wsgi_app, socketio_path=resource) - if self.server.eio.async_mode == 'threading': - app.run(host=host, port=port, threaded=True, - use_reloader=use_reloader) - elif self.server.eio.async_mode == 'eventlet': - import eventlet - eventlet.wsgi.server(eventlet.listen((host, port)), app, - log_output=log_output, **kwargs) - elif self.server.eio.async_mode == 'gevent': - from gevent import pywsgi - log = 'default' - if not log_output: - log = None - pywsgi.WSGIServer((host, port), app, log=log).serve_forever() + if not test_mode: + if self.server.eio.async_mode == 'threading': + app.run(host=host, port=port, threaded=True, + use_reloader=use_reloader) + elif self.server.eio.async_mode == 'eventlet': + import eventlet + eventlet.wsgi.server(eventlet.listen((host, port)), app, + log_output=log_output, **kwargs) + elif self.server.eio.async_mode == 'gevent': + from gevent import pywsgi + log = 'default' + if not log_output: + log = None + pywsgi.WSGIServer((host, port), app, log=log).serve_forever() def test_client(self, app, namespace=None): """Return a simple SocketIO client that can be used for unit tests.""" + if self.server is None: + self.run(app, test_mode=True) return SocketIOTestClient(app, self, namespace) def _copy_session(self, src, dest): diff --git a/setup.py b/setup.py index 4ea82934..a5cd3e2f 100755 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name='Flask-SocketIO', - version='0.9.0', + version='1.0a1', url='http://github.com/miguelgrinberg/Flask-SocketIO/', license='MIT', author='Miguel Grinberg',