Skip to content

Commit

Permalink
Tornado 5 support
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Jun 28, 2018
1 parent 58c8a6f commit 555b69e
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 3 deletions.
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Features
- Compatible with Python 2.7 and Python 3.3+.
- Supports large number of clients even on modest hardware when used with an
asynchronous server based on `asyncio <https://docs.python.org/3/library/asyncio.html>`_
(`sanic <http://sanic.readthedocs.io/>`_ and `aiohttp <http://aiohttp.readthedocs.io/>`_),
(`sanic <http://sanic.readthedocs.io/>`_, `aiohttp <http://aiohttp.readthedocs.io/>`_
or `tornado <http://www.tornadoweb.org/>`_),
`eventlet <http://eventlet.net/>`_ or `gevent <http://gevent.org/>`_. For
development and testing, any WSGI compliant multi-threaded server can also be
used.
Expand Down
10 changes: 10 additions & 0 deletions examples/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ aiohttp
-------

Examples that are compatible with the aiohttp framework for asyncio.

sanic
-----

Examples that are compatible with the sanic framework for asyncio.

tornado
-------

Examples that are compatible with the tornado framework.
101 changes: 101 additions & 0 deletions examples/tornado/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import os

import tornado.ioloop
from tornado.options import define, options, parse_command_line
import tornado.web

import socketio

define("port", default=8888, help="run on the given port", type=int)
define("debug", default=False, help="run in debug mode")

sio = socketio.AsyncServer(async_mode='tornado')


async def background_task():
"""Example of how to send server generated events to clients."""
count = 0
while True:
await sio.sleep(10)
count += 1
await sio.emit('my response', {'data': 'Server generated event'},
namespace='/test')


class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("app.html")


@sio.on('my event', namespace='/test')
async def test_message(sid, message):
await sio.emit('my response', {'data': message['data']}, room=sid,
namespace='/test')


@sio.on('my broadcast event', namespace='/test')
async def test_broadcast_message(sid, message):
await sio.emit('my response', {'data': message['data']}, namespace='/test')


@sio.on('join', namespace='/test')
async def join(sid, message):
sio.enter_room(sid, message['room'], namespace='/test')
await sio.emit('my response', {'data': 'Entered room: ' + message['room']},
room=sid, namespace='/test')


@sio.on('leave', namespace='/test')
async def leave(sid, message):
sio.leave_room(sid, message['room'], namespace='/test')
await sio.emit('my response', {'data': 'Left room: ' + message['room']},
room=sid, namespace='/test')


@sio.on('close room', namespace='/test')
async def close(sid, message):
await sio.emit('my response',
{'data': 'Room ' + message['room'] + ' is closing.'},
room=message['room'], namespace='/test')
await sio.close_room(message['room'], namespace='/test')


@sio.on('my room event', namespace='/test')
async def send_room_message(sid, message):
await sio.emit('my response', {'data': message['data']},
room=message['room'], namespace='/test')


@sio.on('disconnect request', namespace='/test')
async def disconnect_request(sid):
await sio.disconnect(sid, namespace='/test')


@sio.on('connect', namespace='/test')
async def test_connect(sid, environ):
await sio.emit('my response', {'data': 'Connected', 'count': 0}, room=sid,
namespace='/test')


@sio.on('disconnect', namespace='/test')
def test_disconnect(sid):
print('Client disconnected')


def main():
parse_command_line()
app = tornado.web.Application(
[
(r"/", MainHandler),
(r"/socket.io/", socketio.get_tornado_handler(sio)),
],
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
debug=options.debug,
)
app.listen(options.port)
tornado.ioloop.IOLoop.current().start()


if __name__ == "__main__":
main()
41 changes: 41 additions & 0 deletions examples/tornado/latency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import os

import tornado.ioloop
from tornado.options import define, options, parse_command_line
import tornado.web

import socketio

define("port", default=8888, help="run on the given port", type=int)
define("debug", default=False, help="run in debug mode")

sio = socketio.AsyncServer(async_mode='tornado')


class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("latency.html")


@sio.on('ping_from_client')
async def ping(sid):
await sio.emit('pong_from_server', room=sid)


def main():
parse_command_line()
app = tornado.web.Application(
[
(r"/", MainHandler),
(r"/socket.io/", socketio.get_tornado_handler(sio)),
],
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
debug=options.debug,
)
app.listen(options.port)
tornado.ioloop.IOLoop.current().start()


if __name__ == "__main__":
main()
4 changes: 4 additions & 0 deletions examples/tornado/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
tornado==5.0.2
python-engineio
python_socketio
six==1.10.0
4 changes: 4 additions & 0 deletions examples/tornado/static/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
body { margin: 0; padding: 0; font-family: Helvetica Neue; }
h1 { margin: 100px 100px 10px; }
h2 { color: #999; margin: 0 100px 30px; font-weight: normal; }
#latency { color: red; }
91 changes: 91 additions & 0 deletions examples/tornado/templates/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!DOCTYPE HTML>
<html>
<head>
<title>python-socketio test</title>
<script type="text/javascript" src="//code.jquery.com/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.slim.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
namespace = '/test';
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);

socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
socket.on('disconnect', function() {
$('#log').append('<br>Disconnected');
});
socket.on('my response', function(msg) {
$('#log').append('<br>Received: ' + msg.data);
});

// event handler for server sent data
// the data is displayed in the "Received" section of the page
// handlers for the different forms in the page
// these send data to the server in a variety of ways
$('form#emit').submit(function(event) {
socket.emit('my event', {data: $('#emit_data').val()});
return false;
});
$('form#broadcast').submit(function(event) {
socket.emit('my broadcast event', {data: $('#broadcast_data').val()});
return false;
});
$('form#join').submit(function(event) {
socket.emit('join', {room: $('#join_room').val()});
return false;
});
$('form#leave').submit(function(event) {
socket.emit('leave', {room: $('#leave_room').val()});
return false;
});
$('form#send_room').submit(function(event) {
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;
});
});
</script>
</head>
<body>
<h1>python-socketio test</h1>
<h2>Send:</h2>
<form id="emit" method="POST" action='#'>
<input type="text" name="emit_data" id="emit_data" placeholder="Message">
<input type="submit" value="Echo">
</form>
<form id="broadcast" method="POST" action='#'>
<input type="text" name="broadcast_data" id="broadcast_data" placeholder="Message">
<input type="submit" value="Broadcast">
</form>
<form id="join" method="POST" action='#'>
<input type="text" name="join_room" id="join_room" placeholder="Room Name">
<input type="submit" value="Join Room">
</form>
<form id="leave" method="POST" action='#'>
<input type="text" name="leave_room" id="leave_room" placeholder="Room Name">
<input type="submit" value="Leave Room">
</form>
<form id="send_room" method="POST" action='#'>
<input type="text" name="room_name" id="room_name" placeholder="Room Name">
<input type="text" name="room_data" id="room_data" placeholder="Message">
<input type="submit" value="Send to Room">
</form>
<form id="close" method="POST" action="#">
<input type="text" name="close_room" id="close_room" placeholder="Room Name">
<input type="submit" value="Close Room">
</form>
<form id="disconnect" method="POST" action="#">
<input type="submit" value="Disconnect">
</form>
<h2>Receive:</h2>
<div><p id="log"></p></div>
</body>
</html>
64 changes: 64 additions & 0 deletions examples/tornado/templates/latency.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<!doctype html>
<html>
<head>
<title>Socket.IO Latency</title>
<link rel="stylesheet" href="/static/style.css" />
</head>
<body>
<h1>Socket.IO Latency <span id="latency"></span></h1>
<h2 id="transport">(connecting)</h2>
<canvas id="chart" height="200"></canvas>

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/smoothie/1.27.0/smoothie.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.slim.js"></script>
<script>
// socket
var socket = io.connect('http://' + document.domain + ':' + location.port);
var char = $('chart').get(0);
socket.on('connect', function() {
if (chart.getContext) {
render();
window.onresize = render;
}
send();
});
socket.on('pong_from_server', function() {
var latency = new Date - last;
$('#latency').text(latency + 'ms');
if (time)
time.append(+new Date, latency);
setTimeout(send, 100);
});
socket.on('disconnect', function() {
if (smoothie)
smoothie.stop();
$('#transport').text('(disconnected)');
});

var last;
function send() {
last = new Date;
socket.emit('ping_from_client');
$('#transport').text(socket.io.engine.transport.name);
}

// chart
var smoothie;
var time;
function render() {
if (smoothie)
smoothie.stop();
chart.width = document.body.clientWidth;
smoothie = new SmoothieChart();
smoothie.streamTo(chart, 1000);
time = new TimeSeries();
smoothie.addTimeSeries(time, {
strokeStyle: 'rgb(255, 0, 0)',
fillStyle: 'rgba(255, 0, 0, 0.4)',
lineWidth: 2
});
}
</script>
</body>
</html>
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
platforms='any',
install_requires=[
'six>=1.9.0',
'python-engineio>=1.2.1'
'python-engineio>=2.2.0'
],
tests_require=[
'mock',
Expand Down
3 changes: 2 additions & 1 deletion socketio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .zmq_manager import ZmqManager
from .server import Server
from .namespace import Namespace
from .tornado import get_tornado_handler
if sys.version_info >= (3, 5): # pragma: no cover
from .asyncio_server import AsyncServer
from .asyncio_manager import AsyncManager
Expand All @@ -26,4 +27,4 @@
'Namespace']
if AsyncServer is not None: # pragma: no cover
__all__ += ['AsyncServer', 'AsyncNamespace', 'AsyncManager',
'AsyncRedisManager']
'AsyncRedisManager', 'get_tornado_handler']
8 changes: 8 additions & 0 deletions socketio/tornado.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import sys
if sys.version_info >= (3, 5):
from engineio.async_tornado import get_tornado_handler as \
get_engineio_handler


def get_tornado_handler(socketio_server):
return get_engineio_handler(socketio_server.eio)

0 comments on commit 555b69e

Please sign in to comment.