From 0368202f2bfc961be48fdfe55b4429b2baf06149 Mon Sep 17 00:00:00 2001 From: Roy Hyunjin Han Date: Tue, 19 Feb 2013 08:10:35 -0800 Subject: [PATCH] Passed tests --- experiments/callableweakref-tests.py | 170 --------------------------- experiments/callableweakref.py | 52 -------- experiments/t0.py | 6 - experiments/t1.py | 7 -- experiments/t10.py | 16 --- experiments/t11.py | 21 ---- experiments/t12.py | 32 ----- experiments/t2.py | 19 --- experiments/t3.py | 19 --- experiments/t4.py | 26 ---- experiments/t5.py | 30 ----- socketIO_client/__init__.py | 166 ++++++++++++++------------ socketIO_client/tests.py | 3 +- 13 files changed, 92 insertions(+), 475 deletions(-) delete mode 100644 experiments/callableweakref-tests.py delete mode 100644 experiments/callableweakref.py delete mode 100644 experiments/t0.py delete mode 100644 experiments/t1.py delete mode 100644 experiments/t10.py delete mode 100644 experiments/t11.py delete mode 100644 experiments/t12.py delete mode 100644 experiments/t2.py delete mode 100644 experiments/t3.py delete mode 100644 experiments/t4.py delete mode 100644 experiments/t5.py diff --git a/experiments/callableweakref-tests.py b/experiments/callableweakref-tests.py deleted file mode 100644 index c7a95ec..0000000 --- a/experiments/callableweakref-tests.py +++ /dev/null @@ -1,170 +0,0 @@ -# Note 1: The expected behavior of weakref to an instance is that it should -# return None if no other strong references to the instance exist, -# signalling that the instance can be safely garbage collected. -# -# Note 2: The expected behavior of weakref to a method is that it should -# return None if no other strong references to the parent instance exist, -# signalling that the parent instance can be safely garbage collected. -# -# Note 3: The IPython interpreter stores its own references and will -# produce results different from those of the default Python interpreter. -import callableweakref -import weakref -import unittest - - -class TestCallableWeakRef(unittest.TestCase): - - def test_instanceDirectWeakref_dies_on_arrival(self): - 'Assert that weakref works as expected' - instanceDirectWeakref = weakref.ref(Instance()) - assert instanceDirectWeakref() is None - - def test_instanceIndirectWeakref_dies_when_instance_dies(self): - 'Assert that weakref works as expected' - instance = Instance() - instanceIndirectWeakref = weakref.ref(instance) - assert instanceIndirectWeakref() is instance - del instance - assert instanceIndirectWeakref() is None - - def test_boundMethodDirectWeakref_dies_on_arrival(self): - 'Assert that weakref does not work as expected' - instance = Instance() - boundMethodDirectWeakref = weakref.ref(instance.call) - assert boundMethodDirectWeakref() is None # Should be instance - - def test_boundMethodIndirectWeakref_lives_when_instance_dies(self): - 'Assert that weakref works as expected' - instance = Instance() - boundMethod = instance.call - boundMethodIndirectWeakref = weakref.ref(boundMethod) - assert boundMethodIndirectWeakref() is boundMethod - del instance - assert boundMethodIndirectWeakref() is boundMethod - del boundMethod - assert boundMethodIndirectWeakref() is None - - def test_unboundMethodDirectWeakref_dies_on_arrival(self): - 'Assert that weakref does not work as expected' - unboundMethodDirectWeakref = weakref.ref(Instance.call) - assert unboundMethodDirectWeakref() is None # Should be Instance.call - - def test_unboundMethodIndirectWeakref_dies_when_unboundMethod_dies(self): - 'Assert that weakref works as expected' - unboundMethod = Instance.call - unboundMethodIndirectWeakref = weakref.ref(unboundMethod) - assert unboundMethodIndirectWeakref() is unboundMethod - del unboundMethod - assert unboundMethodIndirectWeakref() is None - - def test_classFunctionDirectWeakref_dies_on_arrival(self): - 'Assert that weakref works as expected' - classFunctionDirectWeakref = weakref.ref(Instance()) - assert classFunctionDirectWeakref() is None - - def test_classFunctionIndirectWeakref_dies_when_classFunction_dies(self): - 'Assert that weakref works as expected' - classFunction = Instance() - classFunctionIndirectWeakref = weakref.ref(classFunction) - assert classFunctionIndirectWeakref() is classFunction - del classFunction - assert classFunctionIndirectWeakref() is None - - def test_normalFunctionDirectWeakref_dies_when_normal_function_dies(self): - 'Assert that weakref works as expected' - call = lambda: None - normalFunctionDirectWeakref = weakref.ref(call) - assert normalFunctionDirectWeakref() is call - del call - assert normalFunctionDirectWeakref() is None - - def test_normalFunctionIndirectWeakref_dies_when_normal_function_dies(self): - 'Assert that weakref works as expected' - call = lambda: None - normalFunction = call - normalFunctionIndirectWeakref = weakref.ref(normalFunction) - assert normalFunctionIndirectWeakref() is normalFunction - del normalFunction - assert normalFunctionIndirectWeakref() is call - del call - assert normalFunctionIndirectWeakref() is None - - # Assert that CallableWeakref works as expected for callables - - def test_boundMethodDirectCallableWeakref_dies_when_instance_dies(self): - instance = Instance() - boundMethodDirectCallableWeakref = callableweakref.ref(instance.call) - self.assertEqual(boundMethodDirectCallableWeakref(), instance.call) - del instance - assert boundMethodDirectCallableWeakref() is None - - def test_boundMethodIndirectCallableWeakref_dies_when_instance_dies(self): - instance = Instance() - boundMethod = instance.call - boundMethodIndirectCallableWeakref = callableweakref.ref(boundMethod) - self.assertEqual(boundMethodIndirectCallableWeakref(), boundMethod) - del instance - self.assertEqual(boundMethodIndirectCallableWeakref(), boundMethod) - del boundMethod - assert boundMethodIndirectCallableWeakref() is None - - def test_unboundMethodDirectCallableWeakref_lives_on_arrival(self): - unboundMethodDirectCallableWeakref = callableweakref.ref(Instance.call) - self.assertEqual(unboundMethodDirectCallableWeakref(), Instance.call) - - def test_unboundMethodIndirectCallableWeakref_lives_on_arrival(self): - unboundMethod = Instance.call - unboundMethodIndirectCallableWeakref = callableweakref.ref(unboundMethod) - self.assertEqual(unboundMethodIndirectCallableWeakref(), unboundMethod) - - def test_classMethodIndirectCallableWeakref_lives_on_arrival(self): - classMethod = Instance.call_classmethod - classMethodIndirectCallableWeakref = callableweakref.ref(classMethod) - self.assertEqual(classMethodIndirectCallableWeakref(), classMethod) - - def test_staticMethodIndirectCallableWeakref_lives_on_arrival(self): - staticMethod = Instance.call_staticmethod - staticMethodIndirectCallableWeakref = callableweakref.ref(staticMethod) - self.assertEqual(staticMethodIndirectCallableWeakref(), staticMethod) - - def test_classFunctionDirectCallableWeakref_dies_on_arrival(self): - classFunctionDirectCallableWeakref = callableweakref.ref(Instance()) - assert classFunctionDirectCallableWeakref() is None - - def test_classFunctionIndirectCallableWeakref_dies_when_classFunction_dies(self): - classFunction = Instance() - classFunctionIndirectCallableWeakref = callableweakref.ref(classFunction) - self.assertEqual(classFunctionIndirectCallableWeakref(), classFunction.__call__) - del classFunction - assert classFunctionIndirectCallableWeakref() is None - - def test_normalFunctionDirectCallableWeakref_lives_on_arrival(self): - call = lambda: None - normalFunctionDirectCallableWeakref = callableweakref.ref(call) - self.assertEqual(normalFunctionDirectCallableWeakref(), call) - - def test_normalFunctionIndirectCallableWeakref_lives_on_arrival(self): - call = lambda: None - normalFunction = call - normalFunctionIndirectWeakref = callableweakref.ref(normalFunction) - self.assertEqual(normalFunctionIndirectWeakref(), normalFunction) - del normalFunction - self.assertEqual(normalFunctionIndirectWeakref(), call) - - -class Instance(object): - - def __call__(self): - pass - - def call(self): - pass - - @classmethod - def call_classmethod(Class): - pass - - @staticmethod - def call_staticmethod(): - pass diff --git a/experiments/callableweakref.py b/experiments/callableweakref.py deleted file mode 100644 index 788592c..0000000 --- a/experiments/callableweakref.py +++ /dev/null @@ -1,52 +0,0 @@ -import types -import weakref - - -def ref(function): - return CallableWeakReference(function) - - -class CallableWeakReference(object): - - def __init__(self, function): - 'Create a weak reference to a callable' - try: - if function.im_self: - # We have a bound method or class method - self._reference = weakref.ref(function.im_self) - else: - # We have an unbound method - self._reference = None - self._function = function.im_func - self._class = function.im_class - except AttributeError: - try: - function.func_code - # We have a normal function or static method - self._reference = None - self._function = function - self._class = None - except AttributeError: - function = function.__call__ - # We have a class masquerading as a function - self._reference = weakref.ref(function.im_self) - self._function = function.im_func - self._class = function.im_class - - def __call__(self): - if self.dead: - return - if self._reference: - # We have a bound method - return types.MethodType(self._function, self._reference(), self._class) - elif self._class: - return types.MethodType(self._function, None, self._class) - else: - return self._function - - @property - def dead(self): - if self._reference and not self._reference(): - # We have a bound method whose parent reference has died - return True - return False diff --git a/experiments/t0.py b/experiments/t0.py deleted file mode 100644 index 3980052..0000000 --- a/experiments/t0.py +++ /dev/null @@ -1,6 +0,0 @@ -from socketIO_client import SocketIO - -s = SocketIO('localhost', 8000) -del s -from time import sleep -sleep(3) diff --git a/experiments/t1.py b/experiments/t1.py deleted file mode 100644 index 0955ed6..0000000 --- a/experiments/t1.py +++ /dev/null @@ -1,7 +0,0 @@ -class O(object): - - def __del__(self): - print '__del__()' - - -o = O() diff --git a/experiments/t10.py b/experiments/t10.py deleted file mode 100644 index 24733d9..0000000 --- a/experiments/t10.py +++ /dev/null @@ -1,16 +0,0 @@ -class A(object): - - def __init__(self): - self.b = B(self) - - def __del__(self): - print '__del__()' - - -class B(object): - - def __init__(self, a): - self.a = a - - -a = A() diff --git a/experiments/t11.py b/experiments/t11.py deleted file mode 100644 index 11121de..0000000 --- a/experiments/t11.py +++ /dev/null @@ -1,21 +0,0 @@ -class A(object): - - def __init__(self): - self.c = Common() - self.b = B(self.c) - - def __del__(self): - print '__del__()' - - -class B(object): - - def __init__(self, c): - self.c = c - - -class Common(object): - pass - - -a = A() diff --git a/experiments/t12.py b/experiments/t12.py deleted file mode 100644 index b4e9bb3..0000000 --- a/experiments/t12.py +++ /dev/null @@ -1,32 +0,0 @@ -# Will the destructor of a class be called if its two children have cyclic -# references to each other? Yes. - - -class Parent(object): - - def __init__(self): - self.child = Child() - - def __del__(self): - print 'Parent.__del__()' - - -class Child(object): - - def __init__(self): - self.grandChild = GrandChild(self) - - def __del__(self): - print 'Child.__del__()' - - -class GrandChild(object): - - def __init__(self, parent): - self.parent = parent - - def __del__(self): - print 'GrandChild.__del__()' - - -parent = Parent() diff --git a/experiments/t2.py b/experiments/t2.py deleted file mode 100644 index 3ad124c..0000000 --- a/experiments/t2.py +++ /dev/null @@ -1,19 +0,0 @@ -import weakref - - -class O(object): - - def __init__(self): - self.p = P(self) - - def __del__(self): - print '__del__()' - - -class P(object): - - def __init__(self, parent): - self.parent = weakref.ref(parent) - - -o = O() diff --git a/experiments/t3.py b/experiments/t3.py deleted file mode 100644 index 2de3123..0000000 --- a/experiments/t3.py +++ /dev/null @@ -1,19 +0,0 @@ -class O(object): - - def __init__(self): - self.p = P(self.f) - - def __del__(self): - print '__del__()' - - def f(self): - pass - - -class P(object): - - def __init__(self, parentMethod): - self.parentMethod = parentMethod - - -o = O() diff --git a/experiments/t4.py b/experiments/t4.py deleted file mode 100644 index da46dfe..0000000 --- a/experiments/t4.py +++ /dev/null @@ -1,26 +0,0 @@ -import weakref - - -class O(object): - - def __init__(self): - self.p = P(self.f) - - def __del__(self): - print '__del__()' - - def f(self): - pass - - -class P(object): - - def __init__(self, parentMethod): - self.parentMethod = weakref.ref(parentMethod) - - def show(self): - print self.parentMethod - - -o = O() -o.p.show() # Dead on arrival diff --git a/experiments/t5.py b/experiments/t5.py deleted file mode 100644 index a2ff710..0000000 --- a/experiments/t5.py +++ /dev/null @@ -1,30 +0,0 @@ -import callableweakref - - -class O(object): - - def __init__(self): - self.p = P(self.f) - - def __del__(self): - print '__del__()' - - def f(self): - print 'f()' - - -class P(object): - - def __init__(self, parentMethod): - self.parentMethod = callableweakref.ref(parentMethod) - - def show(self): - print self.parentMethod - - def run(self): - self.parentMethod()() - - -o = O() -o.p.show() -o.p.run() diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index b247fb4..b850b1c 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -47,9 +47,10 @@ class SocketIO(object): def __init__(self, host, port, secure=False, proxies=None): self._socketIO = _SocketIO(host, port, secure, proxies) self._channelByPath = {} + self.define(BaseNamespace) # Define default namespace self._rhythmicThread = _RhythmicThread( - self._socketIO.heartbeatTimeout, + self._socketIO.heartbeatInterval, self._socketIO.send_heartbeat) self._rhythmicThread.start() @@ -65,14 +66,15 @@ class SocketIO(object): self.disconnect() def __del__(self): - self.disconnect() + self.disconnect(force=True) @property def connected(self): - return self._socketIO.connection.connected + return self._socketIO.connected - def disconnect(self, channelPath=''): - self._socketIO.disconnect(channelPath) + def disconnect(self, channelPath='', force=False): + if self.connected: + self._socketIO.disconnect(channelPath, force) if channelPath: del self._channelByPath[channelPath] else: @@ -88,37 +90,23 @@ class SocketIO(object): def get_namespace(self, channelPath=''): return self._channelByPath[channelPath].get_namespace() - def on(self, eventName, eventCallback): - self._callbackByEvent[eventName] = callback + def on(self, eventName, eventCallback, channelPath=''): + return self._channelByPath[channelPath].on(eventName, eventCallback) - def message(self, messageData, callback=None, channelName=''): - if isinstance(messageData, basestring): - code = 3 - data = messageData - else: - code = 4 - data = dumps(messageData) - self._send_packet(code, channelName, data, callback) + def message(self, messageData, messageCallback=None, channelPath=''): + self._socketIO.message(messageData, messageCallback, channelPath) - def emit(self, eventName, *eventArguments): - self._socketIO.emit(eventName, *eventArguments) - code = 5 - callback = None - if eventArguments and callable(eventArguments[-1]): - callback = eventArguments[-1] - eventArguments = eventArguments[:-1] - channelName = eventKeywords.get('channelName', '') - data = dumps(dict(name=eventName, args=eventArguments)) - self._send_packet(code, channelName, data, callback) + def emit(self, eventName, *eventArguments, **eventKeywords): + self._socketIO.emit(eventName, *eventArguments, **eventKeywords) def wait(self, seconds=None, forCallbacks=False): if forCallbacks: - self.__listenerThread.wait_for_callbacks(seconds) + self._listenerThread.wait_for_callbacks(seconds) elif seconds: sleep(seconds) else: try: - while self._socketIO.connected: + while self.connected: sleep(1) except KeyboardInterrupt: pass @@ -132,7 +120,7 @@ class _RhythmicThread(Thread): def __init__(self, intervalInSeconds, call, *args, **kw): super(_RhythmicThread, self).__init__() self.intervalInSeconds = intervalInSeconds - self.call + self.call = call self.args = args self.kw = kw self.done = Event() @@ -176,8 +164,8 @@ class _ListenerThread(Thread): except SocketIOPacketError, error: print error continue - channel = self._channelByPath[channelPath] try: + channel = self._channelByPath[channelPath] delegate = { 0: self.on_disconnect, 1: self.on_connect, @@ -199,7 +187,8 @@ class _ListenerThread(Thread): get_eventCallback('connect')() def on_heartbeat(self, packetID, get_eventCallback, data): - get_eventCallback('heartbeat')() + # get_eventCallback('heartbeat')() + pass def on_message(self, packetID, get_eventCallback, data): get_eventCallback('message')(data) @@ -217,10 +206,10 @@ class _ListenerThread(Thread): dataParts = data.split('+', 1) messageID = int(dataParts[0]) arguments = loads(dataParts[1]) or [] - callback = self._socketIO.get_messageCallback(messageID) - if not callback: + messageCallback = self._socketIO.get_messageCallback(messageID) + if not messageCallback: return - callback(*arguments) + messageCallback(*arguments) if self.waiting.is_set() and not self._socketIO.has_messageCallback: self.cancel() @@ -233,9 +222,6 @@ class _SocketIO(object): 'Low-level interface to remove cyclic references in child threads' messageID = 0 - self.callbackByMessageID = {} - self.callbackByEvent = {} - def __init__(self, host, port, secure, proxies): baseURL = '%s:%d/socket.io/%s' % (host, port, PROTOCOL) @@ -258,47 +244,18 @@ class _SocketIO(object): socketURL = '%s://%s/websocket/%s' % (socketScheme, baseURL, sessionID) self.connection = create_connection(socketURL) self.heartbeatInterval = heartbeatTimeout - 2 + self.callbackByMessageID = {} def __del__(self): - self.connection.close() + self.disconnect(force=True) - def get_channel(self, channelPath): - - def get_channel(self, channelName): - return self.channelByName[channelName] - pass - - def recv_packet(self): - code, packetID, channelName, data = -1, None, None, None - try: - packet = self.connection.recv() - except WebSocketConnectionClosedException: - raise SocketIOConnectionError('Lost connection (Connection closed)') - except socket.timeout: - raise SocketIOConnectionError('Lost connection (Connection timed out)') - try: - packetParts = packet.split(':', 3) - except AttributeError: - raise SocketIOPacketError('Received invalid packet (%s)' % packet) - packetCount = len(packetParts) - if 4 == packetCount: - code, packetID, channelName, data = packetParts - elif 3 == packetCount: - code, packetID, channelName = packetParts - elif 1 == packetCount: # pragma: no cover - code = packetParts[0] - return int(code), packetID, channelName, data - - def send_packet(self, code, channelName='', data='', callback=None): - callbackNumber = self.set_messageCallback(callback) if callback else '' - packetParts = [str(code), callbackNumber, channelName, data] - try: - self.connection.send(':'.join(packetParts)) - except socket.error: - raise SocketIOPacketError('Could not send packet') - - def disconnect(self, channelPath): - self.send_packet(0, channelPath) + def disconnect(self, channelPath='', force=False): + if not self.connected: + return + if channelPath: + self.send_packet(0, channelPath) + elif not force: + self.connection.close() def connect(self, channelPath): self.send_packet(1, channelPath) @@ -310,6 +267,26 @@ class _SocketIO(object): print 'Could not send heartbeat' pass + def message(self, messageData, messageCallback, channelPath): + if isinstance(messageData, basestring): + code = 3 + data = messageData + else: + code = 4 + data = dumps(messageData) + self.send_packet(code, channelPath, data, messageCallback) + + def emit(self, eventName, *eventArguments, **eventKeywords): + code = 5 + if eventArguments and callable(eventArguments[-1]): + messageCallback = eventArguments[-1] + eventArguments = eventArguments[:-1] + else: + messageCallback = None + channelPath = eventKeywords.get('channelPath', '') + data = dumps(dict(name=eventName, args=eventArguments)) + self.send_packet(code, channelPath, data, messageCallback) + def set_messageCallback(self, callback): 'Set callback that will be called after receiving an acknowledgment' self.messageID += 1 @@ -328,6 +305,41 @@ class _SocketIO(object): def has_messageCallback(self): return True if self.callbackByMessageID else False + def recv_packet(self): + code, packetID, channelPath, data = -1, None, None, None + try: + packet = self.connection.recv() + except WebSocketConnectionClosedException: + raise SocketIOConnectionError('Lost connection (Connection closed)') + except socket.timeout: + raise SocketIOConnectionError('Lost connection (Connection timed out)') + except socket.error: + raise SocketIOConnectionError('Lost connection') + try: + packetParts = packet.split(':', 3) + except AttributeError: + raise SocketIOPacketError('Received invalid packet (%s)' % packet) + packetCount = len(packetParts) + if 4 == packetCount: + code, packetID, channelPath, data = packetParts + elif 3 == packetCount: + code, packetID, channelPath = packetParts + elif 1 == packetCount: # pragma: no cover + code = packetParts[0] + return int(code), packetID, channelPath, data + + def send_packet(self, code, channelPath='', data='', messageCallback=None): + callbackNumber = self.set_messageCallback(messageCallback) if messageCallback else '' + packetParts = [str(code), callbackNumber, channelPath, data] + try: + self.connection.send(':'.join(packetParts)) + except socket.error: + raise SocketIOPacketError('Could not send packet') + + @property + def connected(self): + return self.connection.connected + class Channel(object): @@ -356,8 +368,10 @@ class Channel(object): except KeyError: pass # Check callbacks defined explicitly or use on_() - defaultCallback = lambda *eventArguments: self.get_namespace().on_(eventName, *eventArguments) - return getattr(self, 'on_' + eventName.replace(' ', '_'), defaultCallback) + eventNamespace = self.get_namespace() + callbackName = 'on_' + eventName.replace(' ', '_') + defaultCallback = lambda *eventArguments: eventNamespace.on_(eventName, *eventArguments) + return getattr(eventNamespace, callbackName, defaultCallback) class SocketIOError(Exception): diff --git a/socketIO_client/tests.py b/socketIO_client/tests.py index 1cbf3b6..6442e1e 100644 --- a/socketIO_client/tests.py +++ b/socketIO_client/tests.py @@ -12,7 +12,7 @@ class TestSocketIO(TestCase): def test_disconnect(self): socketIO = SocketIO('localhost', 8000) socketIO.disconnect() - self.assertEqual(socketIO.connected, False) + self.assertEqual(False, socketIO.connected) childThreads = [ socketIO._rhythmicThread, socketIO._listenerThread, @@ -78,6 +78,7 @@ class Namespace(BaseNamespace): payload = None def on_ddd(self, data=''): + print '[Event] ddd(%s)' % data self.payload = data