From 18cf130d9e3c38b61e064241bfe638abde3c8a06 Mon Sep 17 00:00:00 2001 From: Roy Hyunjin Han Date: Sun, 15 Feb 2015 10:20:11 -0500 Subject: [PATCH] Fix #40 --- TODO.goals | 1 - socketIO_client/__init__.py | 109 ++++++++++++++++++++++++++---------- socketIO_client/tests.py | 10 +++- 3 files changed, 88 insertions(+), 32 deletions(-) diff --git a/TODO.goals b/TODO.goals index c8d206f..e2023ce 100644 --- a/TODO.goals +++ b/TODO.goals @@ -1,4 +1,3 @@ -Use HTTP headers for Websocket connection #44 Do not connect to blank namespace if overriding default namespace exists #40 Pass wait timeout to underlying transports #34 Check that we have the correct version of websocket #62 diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index bc98d8a..8979801 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -34,9 +34,6 @@ class BaseNamespace(object): self._callback_by_event = {} self.initialize() - def _log(self, level, msg, *attrs): - _log.log(level, '%s: %s' % (self._transport._url, msg), *attrs) - def initialize(self): 'Initialize custom variables here; you can override this method' pass @@ -57,19 +54,19 @@ class BaseNamespace(object): def on_connect(self): 'Called after server connects; you can override this method' - self._log(logging.DEBUG, '%s [connect]', self.path) + pass def on_disconnect(self): 'Called after server disconnects; you can override this method' - self._log(logging.DEBUG, '%s [disconnect]', self.path) + pass def on_heartbeat(self): '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): '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): """ @@ -78,32 +75,28 @@ class BaseNamespace(object): such as one defined by namespace.on('my_event', my_function). """ callback, args = find_callback(args) - arguments = [repr(_) for _ in args] if callback: - arguments.append('callback(*args)') callback(*args) - self._log(logging.INFO, '%s [event] %s(%s)', self.path, event, - ', '.join(arguments)) def on_error(self, reason, advice): '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): '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): - self._log(logging.INFO, '%s [open] %s', self.path, args) + pass def on_close(self, *args): - self._log(logging.INFO, '%s [close] %s', self.path, args) + pass def on_retry(self, *args): - self._log(logging.INFO, '%s [retry] %s', self.path, args) + pass def on_reconnect(self, *args): - self._log(logging.INFO, '%s [reconnect] %s', self.path, args) + pass def _find_event_callback(self, event): # Check callbacks defined by on() @@ -111,15 +104,12 @@ class BaseNamespace(object): return self._callback_by_event[event] except KeyError: 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 self.was_connected == False: + if not self.was_connected: self.was_connected = True else: event = 'reconnect' - # Check callbacks defined explicitly or use on_event() return getattr( self, @@ -127,6 +117,61 @@ class BaseNamespace(object): 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): """Create a socket.io client that connects to a socket.io server @@ -147,14 +192,16 @@ class SocketIO(object): """ def __init__( - self, host, port=None, Namespace=BaseNamespace, - wait_for_connection=True, transports=TRANSPORTS, resource='socket.io', **kw): + self, host, port=None, Namespace=None, + wait_for_connection=True, transports=TRANSPORTS, + resource='socket.io', **kw): self.is_secure, self.base_url = _parse_host(host, port, resource) self.wait_for_connection = wait_for_connection self._namespace_by_path = {} self.client_supported_transports = transports self.kw = kw - self.define(Namespace) + if Namespace: + self.define(Namespace) def log(self, level, msg, *attrs): _log.log(level, '%s: %s' % (self.base_url, msg), @@ -177,6 +224,8 @@ class SocketIO(object): return namespace 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) def message(self, data='', callback=None, path=''): @@ -237,10 +286,12 @@ class SocketIO(object): def disconnect(self, path=''): if self.connected: self._transport.disconnect(path) - namespace = self._namespace_by_path[path] - namespace.on_disconnect() - if path: - del self._namespace_by_path[path] + try: + namespace = self._namespace_by_path[path] + namespace.on_disconnect() + except KeyError: + pass + del self._namespace_by_path[path] @property def connected(self): diff --git a/socketIO_client/tests.py b/socketIO_client/tests.py index 542339e..829902f 100644 --- a/socketIO_client/tests.py +++ b/socketIO_client/tests.py @@ -2,7 +2,7 @@ import logging import time from unittest import TestCase -from . import SocketIO, BaseNamespace, find_callback +from . import SocketIO, LoggingNamespace, find_callback from .transports import TIMEOUT_IN_SECONDS @@ -31,6 +31,7 @@ class BaseMixin(object): def test_disconnect(self): 'Disconnect' + self.socketIO.define(LoggingNamespace) self.assertTrue(self.socketIO.connected) self.socketIO.disconnect() self.assertFalse(self.socketIO.connected) @@ -65,12 +66,14 @@ class BaseMixin(object): def test_message_with_callback(self): 'Message with callback' + self.socketIO.define(LoggingNamespace) self.socketIO.message(callback=self.on_response) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.assertTrue(self.called_on_response) def test_message_with_callback_with_data(self): 'Message with callback with data' + self.socketIO.define(LoggingNamespace) self.socketIO.message(DATA, self.on_response) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.assertTrue(self.called_on_response) @@ -104,12 +107,14 @@ class BaseMixin(object): def test_emit_with_callback(self): 'Emit with callback' + self.socketIO.define(LoggingNamespace) self.socketIO.emit('emit_with_callback', self.on_response) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.assertTrue(self.called_on_response) def test_emit_with_callback_with_payload(self): 'Emit with callback with payload' + self.socketIO.define(LoggingNamespace) self.socketIO.emit( 'emit_with_callback_with_payload', self.on_response) 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): 'Emit with callback with multiple payloads' + self.socketIO.define(LoggingNamespace) self.socketIO.emit( 'emit_with_callback_with_multiple_payloads', self.on_response) 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 -class Namespace(BaseNamespace): +class Namespace(LoggingNamespace): def initialize(self): self.response = None