diff --git a/examples/multiplexed/multiplexed.py b/examples/multiplexed/multiplexed.py index 4c5aedd..ef87488 100644 --- a/examples/multiplexed/multiplexed.py +++ b/examples/multiplexed/multiplexed.py @@ -1,3 +1,5 @@ +from __future__ import print_function + from os import path as op import datetime @@ -31,7 +33,7 @@ def get_username(cls): return 'User%d' % cls.unique_id def on_open(self, info): - print 'Chat', repr(info) + print('Chat', repr(info)) # Give user unique ID self.user_name = self.get_username() @@ -54,7 +56,7 @@ def broadcast(self, msg): class PingConnection(SocketConnection): def on_open(self, info): - print 'Ping', repr(info) + print('Ping', repr(info)) def on_message(self, message): now = datetime.datetime.now() @@ -68,7 +70,7 @@ class RouterConnection(SocketConnection): '/ping': PingConnection} def on_open(self, info): - print 'Router', repr(info) + print('Router', repr(info)) # Create tornadio server MyRouter = TornadioRouter(RouterConnection) diff --git a/examples/rpcping/rpcping.py b/examples/rpcping/rpcping.py index 6a6f57a..7f41e20 100644 --- a/examples/rpcping/rpcping.py +++ b/examples/rpcping/rpcping.py @@ -1,3 +1,5 @@ +from __future__ import print_function + from os import path as op import datetime @@ -24,7 +26,7 @@ def get(self): class PingConnection(SocketConnection): @event def ping(self, client, text): - print 'Got %s from client' % text + print('Got %s from client' % text) now = datetime.datetime.now() diff --git a/tornadio2/conn.py b/tornadio2/conn.py index b58b0b4..40bd949 100644 --- a/tornadio2/conn.py +++ b/tornadio2/conn.py @@ -25,6 +25,7 @@ from inspect import ismethod, getmembers from tornadio2 import proto +from tornadio2.py2compat import with_metaclass logger = logging.getLogger('tornadio2.conn') @@ -62,7 +63,7 @@ class EventMagicMeta(type): """Event handler metaclass""" def __init__(cls, name, bases, attrs): # find events, also in bases - is_event = lambda x: ismethod(x) and hasattr(x, '_event_name') + is_event = lambda x: hasattr(x, '_event_name') events = [(e._event_name, e) for _, e in getmembers(cls, is_event)] setattr(cls, '_events', dict(events)) @@ -70,7 +71,7 @@ def __init__(cls, name, bases, attrs): super(EventMagicMeta, cls).__init__(name, bases, attrs) -class SocketConnection(object): +class SocketConnection(with_metaclass(EventMagicMeta, object)): """Subclass this class and define at least `on_message()` method to make a Socket.IO connection handler. @@ -96,8 +97,6 @@ def test(self, msg): sock.emit('test', {msg:'Hello World'}); """ - __metaclass__ = EventMagicMeta - __endpoints__ = dict() def __init__(self, session, endpoint=None): diff --git a/tornadio2/flashserver.py b/tornadio2/flashserver.py index 85d15de..02dd4dc 100644 --- a/tornadio2/flashserver.py +++ b/tornadio2/flashserver.py @@ -21,7 +21,6 @@ Flash Socket policy server implementation. Merged with minor modifications from the SocketTornad.IO project. """ -from __future__ import with_statement import socket import errno @@ -64,7 +63,7 @@ def connection_ready(self, sock, _fd, _events): while True: try: connection, address = sock.accept() - except socket.error, ex: + except socket.error as ex: if ex[0] not in (errno.EWOULDBLOCK, errno.EAGAIN): raise return diff --git a/tornadio2/persistent.py b/tornadio2/persistent.py index eb6200f..e55cb71 100644 --- a/tornadio2/persistent.py +++ b/tornadio2/persistent.py @@ -144,7 +144,7 @@ def on_message(self, message): try: self.session.raw_message(message) - except Exception, ex: + except Exception as ex: logger.error('Failed to handle message: ' + traceback.format_exc(ex)) # Close session on exception diff --git a/tornadio2/polling.py b/tornadio2/polling.py index d5c3f2f..f5bb503 100644 --- a/tornadio2/polling.py +++ b/tornadio2/polling.py @@ -22,7 +22,10 @@ """ import time import logging -import urllib +try: + from urllib import unquote_plus # Python 2 +except ImportError: + from urllib.parse import unquote_plus # Python 3 from tornado.web import HTTPError, asynchronous @@ -295,7 +298,7 @@ def post(self, session_id): raise HTTPError(403) # Grab data - data = urllib.unquote_plus(data[2:]).decode('utf-8') + data = unquote_plus(data[2:]).decode('utf-8') # If starts with double quote, it is json encoded (socket.io workaround) if data.startswith(u'"'): diff --git a/tornadio2/proto.py b/tornadio2/proto.py index ccf3137..43cd9c9 100644 --- a/tornadio2/proto.py +++ b/tornadio2/proto.py @@ -21,6 +21,7 @@ Socket.IO protocol related functions """ import logging +import sys logger = logging.getLogger('tornadio2.proto') @@ -40,6 +41,14 @@ def default(self, o): return super(DecimalEncoder, self).default(o) json_decimal_args = {"cls": DecimalEncoder} + +if sys.version_info[0] == 2: + text_type = unicode + string_types = (str, unicode) +else: + text_type = str + string_types = (str,) + # Packet ids DISCONNECT = '0' CONNECT = '1' @@ -106,7 +115,7 @@ def message(endpoint, msg, message_id=None, force_json=False): 'message_id': message_id or u''} # Trying to send a dict over the wire ? - if not isinstance(msg, (unicode, str)) and isinstance(msg, (dict, object)): + if not isinstance(msg, string_types) and isinstance(msg, (dict, object)): packed_data.update({'kind': JSON, 'msg': json.dumps(msg, **json_decimal_args)}) @@ -114,7 +123,7 @@ def message(endpoint, msg, message_id=None, force_json=False): # and respect forced JSON if requested else: packed_data.update({'kind': MESSAGE if not force_json else JSON, - 'msg': msg if isinstance(msg, unicode) else str(msg).decode('utf-8')}) + 'msg': msg if isinstance(msg, text_type) else str(msg).decode('utf-8')}) return packed_message_tpl % packed_data @@ -224,7 +233,7 @@ def decode_frames(data): """ # Single message - nothing to decode here - assert isinstance(data, unicode), 'frame is not unicode' + assert isinstance(data, text_type), 'frame is not unicode' if not data.startswith(FRAME_SEPARATOR): return [data] diff --git a/tornadio2/router.py b/tornadio2/router.py index 61e5652..4e39ee4 100644 --- a/tornadio2/router.py +++ b/tornadio2/router.py @@ -87,7 +87,7 @@ def get(self, version, *args, **kwargs): # TODO: Fix heartbeat timeout. For now, it is adding 5 seconds to the client timeout. data = '%s:%d:%d:%s' % ( - sess.session_id, + sess.session_id.decode('utf-8'), # TODO: Fix me somehow a well. 0.9.2 will drop connection is no # heartbeat was sent over settings['heartbeat_interval'] + settings['client_timeout'], diff --git a/tornadio2/server.py b/tornadio2/server.py index 98a7802..c08a2cd 100644 --- a/tornadio2/server.py +++ b/tornadio2/server.py @@ -99,7 +99,7 @@ def __init__(self, application, io_loop=io_loop, port=flash_policy_port, policy_file=flash_policy_file) - except Exception, ex: + except Exception as ex: logger.error('Failed to start Flash policy server: %s', ex) if auto_start: diff --git a/tornadio2/session.py b/tornadio2/session.py index caedb15..747cd0b 100644 --- a/tornadio2/session.py +++ b/tornadio2/session.py @@ -21,7 +21,10 @@ Active TornadIO2 connection session. """ -import urlparse +try: + from urlparse import urlparse # Python 2 +except ImportError: + from urllib.parse import urlparse # Python 3 import logging @@ -289,7 +292,7 @@ def connect_endpoint(self, url): `url` socket.io endpoint URL. """ - urldata = urlparse.urlparse(url) + urldata = urlparse(url) endpoint = urldata.path @@ -404,7 +407,7 @@ def raw_message(self, msg): # in args if len(args) == 1 and isinstance(args[0], dict): # Fix for the http://bugs.python.org/issue4978 for older Python versions - str_args = dict((str(x), y) for x, y in args[0].iteritems()) + str_args = dict((str(x), y) for x, y in args[0].items()) ack_response = conn.on_event(event['name'], kwargs=str_args) else: @@ -429,7 +432,7 @@ def raw_message(self, msg): logger.error('Incoming error: %s' % msg_data) elif msg_type == proto.NOOP: pass - except Exception, ex: + except Exception as ex: logger.exception(ex) # TODO: Add global exception callback? diff --git a/tornadio2/sessioncontainer.py b/tornadio2/sessioncontainer.py index 76886ed..c389804 100644 --- a/tornadio2/sessioncontainer.py +++ b/tornadio2/sessioncontainer.py @@ -31,8 +31,9 @@ def _random_key(): """Return random session key""" i = md5() - i.update('%s%s' % (random(), time())) - return i.hexdigest() + # Python 3 requires bytes not string, hence the encode + i.update(('%s%s' % (random(), time())).encode('utf-8')) + return i.hexdigest().encode('utf-8') class SessionBase(object): @@ -71,9 +72,14 @@ def on_delete(self, forced): """Triggered when object was expired or deleted.""" pass + # Python 2 def __cmp__(self, other): return cmp(self.expiry_date, other.expiry_date) + # Python 3 + def __lt__(self, other): + return self.expiry_date < other.expiry_date + def __repr__(self): return '%f %s %d' % (getattr(self, 'expiry_date', -1), self.session_id,