This commit is contained in:
Roy Hyunjin Han 2015-02-15 10:20:11 -05:00
commit 18cf130d9e
3 changed files with 88 additions and 32 deletions

View file

@ -1,4 +1,3 @@
Use HTTP headers for Websocket connection #44
Do not connect to blank namespace if overriding default namespace exists #40 Do not connect to blank namespace if overriding default namespace exists #40
Pass wait timeout to underlying transports #34 Pass wait timeout to underlying transports #34
Check that we have the correct version of websocket #62 Check that we have the correct version of websocket #62

View file

@ -34,9 +34,6 @@ class BaseNamespace(object):
self._callback_by_event = {} self._callback_by_event = {}
self.initialize() self.initialize()
def _log(self, level, msg, *attrs):
_log.log(level, '%s: %s' % (self._transport._url, msg), *attrs)
def initialize(self): def initialize(self):
'Initialize custom variables here; you can override this method' 'Initialize custom variables here; you can override this method'
pass pass
@ -57,19 +54,19 @@ class BaseNamespace(object):
def on_connect(self): def on_connect(self):
'Called after server connects; you can override this method' 'Called after server connects; you can override this method'
self._log(logging.DEBUG, '%s [connect]', self.path) pass
def on_disconnect(self): def on_disconnect(self):
'Called after server disconnects; you can override this method' 'Called after server disconnects; you can override this method'
self._log(logging.DEBUG, '%s [disconnect]', self.path) pass
def on_heartbeat(self): def on_heartbeat(self):
'Called after server sends a heartbeat; you can override this method' 'Called after server sends a heartbeat; you can override this method'
self._log(logging.DEBUG, '%s [heartbeat]', self.path) pass
def on_message(self, data): def on_message(self, data):
'Called after server sends a message; you can override this method' 'Called after server sends a message; you can override this method'
self._log(logging.INFO, '%s [message] %s', self.path, data) pass
def on_event(self, event, *args): def on_event(self, event, *args):
""" """
@ -78,32 +75,28 @@ class BaseNamespace(object):
such as one defined by namespace.on('my_event', my_function). such as one defined by namespace.on('my_event', my_function).
""" """
callback, args = find_callback(args) callback, args = find_callback(args)
arguments = [repr(_) for _ in args]
if callback: if callback:
arguments.append('callback(*args)')
callback(*args) callback(*args)
self._log(logging.INFO, '%s [event] %s(%s)', self.path, event,
', '.join(arguments))
def on_error(self, reason, advice): def on_error(self, reason, advice):
'Called after server sends an error; you can override this method' 'Called after server sends an error; you can override this method'
self._log(logging.INFO, '%s [error] %s', self.path, advice) pass
def on_noop(self): def on_noop(self):
'Called after server sends a noop; you can override this method' 'Called after server sends a noop; you can override this method'
self._log(logging.INFO, '%s [noop]', self.path) pass
def on_open(self, *args): def on_open(self, *args):
self._log(logging.INFO, '%s [open] %s', self.path, args) pass
def on_close(self, *args): def on_close(self, *args):
self._log(logging.INFO, '%s [close] %s', self.path, args) pass
def on_retry(self, *args): def on_retry(self, *args):
self._log(logging.INFO, '%s [retry] %s', self.path, args) pass
def on_reconnect(self, *args): def on_reconnect(self, *args):
self._log(logging.INFO, '%s [reconnect] %s', self.path, args) pass
def _find_event_callback(self, event): def _find_event_callback(self, event):
# Check callbacks defined by on() # Check callbacks defined by on()
@ -111,15 +104,12 @@ class BaseNamespace(object):
return self._callback_by_event[event] return self._callback_by_event[event]
except KeyError: except KeyError:
pass pass
# Convert connect to reconnect if we have seen connect already
# Convert connect to reconnect if we have seen connect
# already.
if event == 'connect': if event == 'connect':
if self.was_connected == False: if not self.was_connected:
self.was_connected = True self.was_connected = True
else: else:
event = 'reconnect' event = 'reconnect'
# Check callbacks defined explicitly or use on_event() # Check callbacks defined explicitly or use on_event()
return getattr( return getattr(
self, self,
@ -127,6 +117,61 @@ class BaseNamespace(object):
lambda *args: self.on_event(event, *args)) lambda *args: self.on_event(event, *args))
class LoggingNamespace(BaseNamespace):
def _log(self, level, msg, *attrs):
_log.log(level, '%s: %s' % (self._transport._url, msg), *attrs)
def on_connect(self):
self._log(logging.DEBUG, '%s [connect]', self.path)
super(LoggingNamespace, self).on_connect()
def on_disconnect(self):
self._log(logging.DEBUG, '%s [disconnect]', self.path)
super(LoggingNamespace, self).on_disconnect()
def on_heartbeat(self):
self._log(logging.DEBUG, '%s [heartbeat]', self.path)
super(LoggingNamespace, self).on_heartbeat()
def on_message(self, data):
self._log(logging.INFO, '%s [message] %s', self.path, data)
super(LoggingNamespace, self).on_message(data)
def on_event(self, event, *args):
callback, args = find_callback(args)
arguments = [repr(_) for _ in args]
if callback:
arguments.append('callback(*args)')
self._log(logging.INFO, '%s [event] %s(%s)', self.path, event,
', '.join(arguments))
super(LoggingNamespace, self).on_event(event, *args)
def on_error(self, reason, advice):
self._log(logging.INFO, '%s [error] %s', self.path, advice)
super(LoggingNamespace, self).on_error(reason, advice)
def on_noop(self):
self._log(logging.INFO, '%s [noop]', self.path)
super(LoggingNamespace, self).on_noop()
def on_open(self, *args):
self._log(logging.INFO, '%s [open] %s', self.path, args)
super(LoggingNamespace, self).on_open(*args)
def on_close(self, *args):
self._log(logging.INFO, '%s [close] %s', self.path, args)
super(LoggingNamespace, self).on_close(*args)
def on_retry(self, *args):
self._log(logging.INFO, '%s [retry] %s', self.path, args)
super(LoggingNamespace, self).on_retry(*args)
def on_reconnect(self, *args):
self._log(logging.INFO, '%s [reconnect] %s', self.path, args)
super(LoggingNamespace, self).on_reconnect(*args)
class SocketIO(object): class SocketIO(object):
"""Create a socket.io client that connects to a socket.io server """Create a socket.io client that connects to a socket.io server
@ -147,14 +192,16 @@ class SocketIO(object):
""" """
def __init__( def __init__(
self, host, port=None, Namespace=BaseNamespace, self, host, port=None, Namespace=None,
wait_for_connection=True, transports=TRANSPORTS, resource='socket.io', **kw): wait_for_connection=True, transports=TRANSPORTS,
resource='socket.io', **kw):
self.is_secure, self.base_url = _parse_host(host, port, resource) self.is_secure, self.base_url = _parse_host(host, port, resource)
self.wait_for_connection = wait_for_connection self.wait_for_connection = wait_for_connection
self._namespace_by_path = {} self._namespace_by_path = {}
self.client_supported_transports = transports self.client_supported_transports = transports
self.kw = kw self.kw = kw
self.define(Namespace) if Namespace:
self.define(Namespace)
def log(self, level, msg, *attrs): def log(self, level, msg, *attrs):
_log.log(level, '%s: %s' % (self.base_url, msg), _log.log(level, '%s: %s' % (self.base_url, msg),
@ -177,6 +224,8 @@ class SocketIO(object):
return namespace return namespace
def on(self, event, callback, path=''): def on(self, event, callback, path=''):
if path not in self._namespace_by_path:
self.define(BaseNamespace, path)
return self.get_namespace(path).on(event, callback) return self.get_namespace(path).on(event, callback)
def message(self, data='', callback=None, path=''): def message(self, data='', callback=None, path=''):
@ -237,10 +286,12 @@ class SocketIO(object):
def disconnect(self, path=''): def disconnect(self, path=''):
if self.connected: if self.connected:
self._transport.disconnect(path) self._transport.disconnect(path)
namespace = self._namespace_by_path[path] try:
namespace.on_disconnect() namespace = self._namespace_by_path[path]
if path: namespace.on_disconnect()
del self._namespace_by_path[path] except KeyError:
pass
del self._namespace_by_path[path]
@property @property
def connected(self): def connected(self):

View file

@ -2,7 +2,7 @@ import logging
import time import time
from unittest import TestCase from unittest import TestCase
from . import SocketIO, BaseNamespace, find_callback from . import SocketIO, LoggingNamespace, find_callback
from .transports import TIMEOUT_IN_SECONDS from .transports import TIMEOUT_IN_SECONDS
@ -31,6 +31,7 @@ class BaseMixin(object):
def test_disconnect(self): def test_disconnect(self):
'Disconnect' 'Disconnect'
self.socketIO.define(LoggingNamespace)
self.assertTrue(self.socketIO.connected) self.assertTrue(self.socketIO.connected)
self.socketIO.disconnect() self.socketIO.disconnect()
self.assertFalse(self.socketIO.connected) self.assertFalse(self.socketIO.connected)
@ -65,12 +66,14 @@ class BaseMixin(object):
def test_message_with_callback(self): def test_message_with_callback(self):
'Message with callback' 'Message with callback'
self.socketIO.define(LoggingNamespace)
self.socketIO.message(callback=self.on_response) self.socketIO.message(callback=self.on_response)
self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds)
self.assertTrue(self.called_on_response) self.assertTrue(self.called_on_response)
def test_message_with_callback_with_data(self): def test_message_with_callback_with_data(self):
'Message with callback with data' 'Message with callback with data'
self.socketIO.define(LoggingNamespace)
self.socketIO.message(DATA, self.on_response) self.socketIO.message(DATA, self.on_response)
self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds)
self.assertTrue(self.called_on_response) self.assertTrue(self.called_on_response)
@ -104,12 +107,14 @@ class BaseMixin(object):
def test_emit_with_callback(self): def test_emit_with_callback(self):
'Emit with callback' 'Emit with callback'
self.socketIO.define(LoggingNamespace)
self.socketIO.emit('emit_with_callback', self.on_response) self.socketIO.emit('emit_with_callback', self.on_response)
self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds)
self.assertTrue(self.called_on_response) self.assertTrue(self.called_on_response)
def test_emit_with_callback_with_payload(self): def test_emit_with_callback_with_payload(self):
'Emit with callback with payload' 'Emit with callback with payload'
self.socketIO.define(LoggingNamespace)
self.socketIO.emit( self.socketIO.emit(
'emit_with_callback_with_payload', self.on_response) 'emit_with_callback_with_payload', self.on_response)
self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds)
@ -117,6 +122,7 @@ class BaseMixin(object):
def test_emit_with_callback_with_multiple_payloads(self): def test_emit_with_callback_with_multiple_payloads(self):
'Emit with callback with multiple payloads' 'Emit with callback with multiple payloads'
self.socketIO.define(LoggingNamespace)
self.socketIO.emit( self.socketIO.emit(
'emit_with_callback_with_multiple_payloads', self.on_response) 'emit_with_callback_with_multiple_payloads', self.on_response)
self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds)
@ -205,7 +211,7 @@ class Test_JSONP_PollingTransport(TestCase, BaseMixin):
self.wait_time_in_seconds = TIMEOUT_IN_SECONDS + 1 self.wait_time_in_seconds = TIMEOUT_IN_SECONDS + 1
class Namespace(BaseNamespace): class Namespace(LoggingNamespace):
def initialize(self): def initialize(self):
self.response = None self.response = None