From 5ccb32f338d8816a2d655b99b2510024385cd321 Mon Sep 17 00:00:00 2001 From: Roy Hyunjin Han Date: Mon, 18 Nov 2013 10:43:31 -0800 Subject: [PATCH] Exit the loop if the client wants to disconnect; fixes #30 --- CHANGES.rst | 5 +++++ serve_tests.js | 3 +++ setup.py | 2 +- socketIO_client/__init__.py | 19 +++++++++++++++++-- socketIO_client/tests.py | 21 +++++++++++++++++---- socketIO_client/transports.py | 3 +++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 41058f9..7e91a66 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,8 @@ +0.5.3 +----- +- Exit the wait loop if the client wants to disconnect +- Set heartbeat_interval to be half of the heartbeat_timeout + 0.5.2 ----- - Replaced secure=True with host='https://example.com' diff --git a/serve_tests.js b/serve_tests.js index 9107d4a..6c2edfc 100644 --- a/serve_tests.js +++ b/serve_tests.js @@ -48,6 +48,9 @@ var main = io.of('').on('connection', function(socket) { fn(payload); } }); + socket.on('wait_with_disconnect', function() { + socket.emit('wait_with_disconnect_response'); + }); }); var chat = io.of('/chat').on('connection', function (socket) { diff --git a/setup.py b/setup.py index 507323d..639cb7d 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ CHANGES = open(os.path.join(here, 'CHANGES.rst')).read() setup( name='socketIO-client', - version='0.5.2', + version='0.5.3', description='A socket.io client library', long_description=README + '\n\n' + CHANGES, license='MIT', diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index af65fca..5faa483 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -39,6 +39,9 @@ class BaseNamespace(object): callback, args = find_callback(args, kw) self._transport.emit(self.path, event, args, callback) + def disconnect(self): + self._transport.disconnect(self.path) + def on(self, event, callback): 'Define a callback to handle a custom event emitted by the server' self._callback_by_event[event] = callback @@ -161,12 +164,14 @@ class SocketIO(object): self._transport.emit(path, event, args, callback) def wait(self, seconds=None, for_callbacks=False): + """Wait in a loop and process events as defined in the namespaces. + + - Omit seconds, i.e. call wait() without arguments, to wait forever. + """ try: warning_screen = _yield_warning_screen(seconds) for elapsed_time in warning_screen: try: - if for_callbacks and not self._transport.has_ack_callback: - break try: for packet in self._transport.recv_packet(): try: @@ -175,6 +180,8 @@ class SocketIO(object): _log.warn('[packet error] %s', e) except TimeoutError: pass + if self._stop_waiting(for_callbacks): + break self.heartbeat_pacemaker.send(elapsed_time) except ConnectionError as e: try: @@ -186,6 +193,14 @@ class SocketIO(object): except KeyboardInterrupt: pass + def _stop_waiting(self, for_callbacks): + # Use __transport to make sure that we do not reconnect inadvertently + if for_callbacks and not self.__transport.has_ack_callback: + return True + if self.__transport._wants_to_disconnect: + return True + return False + def wait_for_callbacks(self, seconds=None): self.wait(seconds, for_callbacks=True) diff --git a/socketIO_client/tests.py b/socketIO_client/tests.py index db96990..dfebecb 100644 --- a/socketIO_client/tests.py +++ b/socketIO_client/tests.py @@ -1,4 +1,5 @@ import logging +import time from unittest import TestCase from . import SocketIO, BaseNamespace, find_callback @@ -109,15 +110,15 @@ class BaseMixin(object): def test_emit_with_callback_with_payload(self): 'Emit with callback with payload' - self.socketIO.emit('emit_with_callback_with_payload', - self.on_response) + self.socketIO.emit( + 'emit_with_callback_with_payload', 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_multiple_payloads(self): 'Emit with callback with multiple payloads' - self.socketIO.emit('emit_with_callback_with_multiple_payloads', - self.on_response) + self.socketIO.emit( + 'emit_with_callback_with_multiple_payloads', self.on_response) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.assertTrue(self.called_on_response) @@ -138,6 +139,15 @@ class BaseMixin(object): 'ack_callback_response': (PAYLOAD,), }) + def test_wait_with_disconnect(self): + 'Exit loop when the client wants to disconnect' + self.socketIO.define(Namespace) + self.socketIO.emit('wait_with_disconnect') + timeout_in_seconds = 5 + start_time = time.time() + self.socketIO.wait(timeout_in_seconds) + self.assertTrue(time.time() - start_time < timeout_in_seconds) + def test_namespace_emit(self): 'Behave differently in different namespaces' main_namespace = self.socketIO.define(Namespace) @@ -204,3 +214,6 @@ class Namespace(BaseNamespace): if callback: callback(*args) self.args_by_event[event] = args + + def on_wait_with_disconnect_response(self): + self.disconnect() diff --git a/socketIO_client/transports.py b/socketIO_client/transports.py index 758f34e..b28a1ee 100644 --- a/socketIO_client/transports.py +++ b/socketIO_client/transports.py @@ -22,8 +22,11 @@ class _AbstractTransport(object): def __init__(self): self._packet_id = 0 self._callback_by_packet_id = {} + self._wants_to_disconnect = False def disconnect(self, path=''): + if not path: + self._wants_to_disconnect = True if not self.connected: return if path: