From e16d5df82f52c66ecfb9d5c1bdb9f8dd486e3ee2 Mon Sep 17 00:00:00 2001 From: bartbroere Date: Thu, 15 Sep 2016 18:58:35 +0200 Subject: [PATCH 01/10] Fixed arguments can now be passed to a function (still untested code however) This is useful for pusher messages that include variables in the name of the message (or the channel for that matter), instead of in the data. When setting the callback, some keyword arguments can be provided that should be supplied with each message. In my codebase this change prevented that I have to do one of the following: - have lots of functions with repeating code, - write wrapper functions, - define lots of functions with lambdas in for loops. In an ideal world this change would not be necessary, but in a lot of practical use cases, it is nice to have it. Furthermore, this change is nearly completely backwards compatible, unless an implementation uses the variables in the spawned instance directly. --- pusherclient/channel.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pusherclient/channel.py b/pusherclient/channel.py index a09fe22..6953352 100755 --- a/pusherclient/channel.py +++ b/pusherclient/channel.py @@ -6,7 +6,7 @@ def __init__(self, channel_name, connection): self.event_callbacks = {} - def bind(self, event_name, callback): + def bind(self, event_name, callback, kwargs=None): """Bind an event to a callback :param event_name: The name of the event to bind to. @@ -17,10 +17,11 @@ def bind(self, event_name, callback): if event_name not in self.event_callbacks.keys(): self.event_callbacks[event_name] = [] - self.event_callbacks[event_name].append(callback) + self.event_callbacks[event_name].append({"func": callback, + "kwargs": kwargs}) def trigger(self, event_name, data): - """Trigger an event on this channel. Only available for private or + """Trigger an event on this channel. Only available for private or presence channels :param event_name: The name of the event. Must begin with 'client-'' @@ -36,4 +37,4 @@ def trigger(self, event_name, data): def _handle_event(self, event_name, data): if event_name in self.event_callbacks.keys(): for callback in self.event_callbacks[event_name]: - callback(data) + callback["func"](data, **callback["kwargs"]) From dd85cb6f357c454df3f24ec5e913f300969e755b Mon Sep 17 00:00:00 2001 From: bartbroere Date: Thu, 15 Sep 2016 21:55:19 +0200 Subject: [PATCH 02/10] Added changes to connection.py too (still untested) --- pusherclient/connection.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pusherclient/connection.py b/pusherclient/connection.py index 75fe53f..999c2f2 100755 --- a/pusherclient/connection.py +++ b/pusherclient/connection.py @@ -59,7 +59,7 @@ def __init__(self, event_handler, url, log_level=logging.INFO, daemon=True, reco Thread.__init__(self) self.daemon = daemon - def bind(self, event_name, callback): + def bind(self, event_name, callback, kwargs=None): """Bind an event to a callback :param event_name: The name of the event to bind to. @@ -71,7 +71,8 @@ def bind(self, event_name, callback): if event_name not in self.event_callbacks.keys(): self.event_callbacks[event_name] = [] - self.event_callbacks[event_name].append(callback) + self.event_callbacks[event_name].append({"func": callback, + "kwargs": kwargs}) def disconnect(self): self.needs_reconnect = False @@ -145,7 +146,8 @@ def _on_message(self, ws, message): if params['event'] in self.event_callbacks.keys(): for callback in self.event_callbacks[params['event']]: try: - callback(params['data']) + callback["func"](params['data'], + **callback["kwargs"]) except Exception: self.logger.exception("Callback raised unhandled") else: From 2d3188c0e20f92e020b0e3a88b03289556bda1b3 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Fri, 16 Sep 2016 07:34:39 +0200 Subject: [PATCH 03/10] None no longer default, because ** can't handle that --- pusherclient/channel.py | 80 +++--- pusherclient/connection.py | 562 ++++++++++++++++++------------------- 2 files changed, 321 insertions(+), 321 deletions(-) diff --git a/pusherclient/channel.py b/pusherclient/channel.py index 6953352..8ad3b1a 100755 --- a/pusherclient/channel.py +++ b/pusherclient/channel.py @@ -1,40 +1,40 @@ -class Channel(object): - def __init__(self, channel_name, connection): - self.name = channel_name - - self.connection = connection - - self.event_callbacks = {} - - def bind(self, event_name, callback, kwargs=None): - """Bind an event to a callback - - :param event_name: The name of the event to bind to. - :type event_name: str - - :param callback: The callback to notify of this event. - """ - if event_name not in self.event_callbacks.keys(): - self.event_callbacks[event_name] = [] - - self.event_callbacks[event_name].append({"func": callback, - "kwargs": kwargs}) - - def trigger(self, event_name, data): - """Trigger an event on this channel. Only available for private or - presence channels - - :param event_name: The name of the event. Must begin with 'client-'' - :type event_name: str - - :param data: The data to send with the event. - """ - if self.connection: - if event_name.startswith("client-"): - if self.name.startswith("private-") or self.name.startswith("presence-"): - self.connection.send_event(event_name, data, channel_name=self.name) - - def _handle_event(self, event_name, data): - if event_name in self.event_callbacks.keys(): - for callback in self.event_callbacks[event_name]: - callback["func"](data, **callback["kwargs"]) +class Channel(object): + def __init__(self, channel_name, connection): + self.name = channel_name + + self.connection = connection + + self.event_callbacks = {} + + def bind(self, event_name, callback, kwargs={}): + """Bind an event to a callback + + :param event_name: The name of the event to bind to. + :type event_name: str + + :param callback: The callback to notify of this event. + """ + if event_name not in self.event_callbacks.keys(): + self.event_callbacks[event_name] = [] + + self.event_callbacks[event_name].append({"func": callback, + "kwargs": kwargs}) + + def trigger(self, event_name, data): + """Trigger an event on this channel. Only available for private or + presence channels + + :param event_name: The name of the event. Must begin with 'client-'' + :type event_name: str + + :param data: The data to send with the event. + """ + if self.connection: + if event_name.startswith("client-"): + if self.name.startswith("private-") or self.name.startswith("presence-"): + self.connection.send_event(event_name, data, channel_name=self.name) + + def _handle_event(self, event_name, data): + if event_name in self.event_callbacks.keys(): + for callback in self.event_callbacks[event_name]: + callback["func"](data, **callback["kwargs"]) diff --git a/pusherclient/connection.py b/pusherclient/connection.py index 999c2f2..9a8c8f8 100755 --- a/pusherclient/connection.py +++ b/pusherclient/connection.py @@ -1,281 +1,281 @@ -from threading import Thread, Timer -import websocket -import logging -import time - -try: - import simplejson as json -except ImportError: - import json - - -class Connection(Thread): - def __init__(self, event_handler, url, log_level=logging.INFO, daemon=True, reconnect_interval=10): - self.event_handler = event_handler - self.url = url - - self.socket = None - self.socket_id = "" - - self.event_callbacks = {} - - self.disconnect_called = False - self.needs_reconnect = False - self.default_reconnect_interval = reconnect_interval - self.reconnect_interval = reconnect_interval - - self.pong_timer = None - self.pong_received = False - self.pong_timeout = 30 - - self.bind("pusher:connection_established", self._connect_handler) - self.bind("pusher:connection_failed", self._failed_handler) - self.bind("pusher:pong", self._pong_handler) - self.bind("pusher:ping", self._ping_handler) - self.bind("pusher:error", self._pusher_error_handler) - - self.state = "initialized" - - self.logger = logging.getLogger(self.__module__) # create a new logger - if log_level == logging.DEBUG: - websocket.enableTrace(True) - self.logger.setLevel(log_level) - - # From Martyn's comment at: - # https://pusher.tenderapp.com/discussions/problems/36-no-messages-received-after-1-idle-minute-heartbeat - # "We send a ping every 5 minutes in an attempt to keep connections - # alive..." - # This is why we set the connection timeout to 5 minutes, since we can - # expect a pusher heartbeat message every 5 minutes. Adding 5 sec to - # account for small timing delays which may cause messages to not be - # received in exact 5 minute intervals. - - self.connection_timeout = 305 - self.connection_timer = None - - self.ping_interval = 120 - self.ping_timer = None - - Thread.__init__(self) - self.daemon = daemon - - def bind(self, event_name, callback, kwargs=None): - """Bind an event to a callback - - :param event_name: The name of the event to bind to. - :type event_name: str - - :param callback: The callback to notify of this event. - """ - - if event_name not in self.event_callbacks.keys(): - self.event_callbacks[event_name] = [] - - self.event_callbacks[event_name].append({"func": callback, - "kwargs": kwargs}) - - def disconnect(self): - self.needs_reconnect = False - self.disconnect_called = True - if self.socket: - self.socket.close() - self.join() - - def reconnect(self, reconnect_interval=None): - if reconnect_interval is None: - reconnect_interval = self.default_reconnect_interval - - self.logger.info("Connection: Reconnect in %s" % reconnect_interval) - self.reconnect_interval = reconnect_interval - - self.needs_reconnect = True - if self.socket: - self.socket.close() - - def run(self): - self._connect() - - def _connect(self): - self.state = "connecting" - - self.socket = websocket.WebSocketApp( - self.url, - on_open=self._on_open, - on_message=self._on_message, - on_error=self._on_error, - on_close=self._on_close - ) - - self.socket.run_forever() - - while self.needs_reconnect and not self.disconnect_called: - self.logger.info("Attempting to connect again in %s seconds." - % self.reconnect_interval) - self.state = "unavailable" - time.sleep(self.reconnect_interval) - - # We need to set this flag since closing the socket will set it to - # false - self.socket.keep_running = True - self.socket.run_forever() - - def _on_open(self, ws): - self.logger.info("Connection: Connection opened") - # Send a ping right away to inform that the connection is alive. If you - # don't do this, it takes the ping interval to subcribe to channel and - # events - self.send_ping() - self._start_timers() - - def _on_error(self, ws, error): - self.logger.info("Connection: Error - %s" % error) - self.state = "failed" - self.needs_reconnect = True - - def _on_message(self, ws, message): - self.logger.info("Connection: Message - %s" % message) - - # Stop our timeout timer, since we got some data - self._stop_timers() - - params = self._parse(message) - - if 'event' in params.keys(): - if 'channel' not in params.keys(): - # We've got a connection event. Lets handle it. - if params['event'] in self.event_callbacks.keys(): - for callback in self.event_callbacks[params['event']]: - try: - callback["func"](params['data'], - **callback["kwargs"]) - except Exception: - self.logger.exception("Callback raised unhandled") - else: - self.logger.info("Connection: Unhandled event") - else: - # We've got a channel event. Lets pass it up to the pusher - # so it can be handled by the appropriate channel. - self.event_handler( - params['event'], - params['data'], - params['channel'] - ) - - # We've handled our data, so restart our connection timeout handler - self._start_timers() - - def _on_close(self, ws, *args): - self.logger.info("Connection: Connection closed") - self.state = "disconnected" - self._stop_timers() - - @staticmethod - def _parse(message): - return json.loads(message) - - def _stop_timers(self): - if self.ping_timer: - self.ping_timer.cancel() - - if self.connection_timer: - self.connection_timer.cancel() - - if self.pong_timer: - self.pong_timer.cancel() - - def _start_timers(self): - self._stop_timers() - - self.ping_timer = Timer(self.ping_interval, self.send_ping) - self.ping_timer.start() - - self.connection_timer = Timer(self.connection_timeout, self._connection_timed_out) - self.connection_timer.start() - - def send_event(self, event_name, data, channel_name=None): - event = {'event': event_name, 'data': data} - if channel_name: - event['channel'] = channel_name - - self.logger.info("Connection: Sending event - %s" % event) - try: - self.socket.send(json.dumps(event)) - except Exception as e: - self.logger.error("Failed send event: %s" % e) - - def send_ping(self): - self.logger.info("Connection: ping to pusher") - try: - self.socket.send(json.dumps({'event': 'pusher:ping', 'data': ''})) - except Exception as e: - self.logger.error("Failed send ping: %s" % e) - self.pong_timer = Timer(self.pong_timeout, self._check_pong) - self.pong_timer.start() - - def send_pong(self): - self.logger.info("Connection: pong to pusher") - try: - self.socket.send(json.dumps({'event': 'pusher:pong', 'data': ''})) - except Exception as e: - self.logger.error("Failed send pong: %s" % e) - - def _check_pong(self): - self.pong_timer.cancel() - - if self.pong_received: - self.pong_received = False - else: - self.logger.info("Did not receive pong in time. Will attempt to reconnect.") - self.state = "failed" - self.reconnect() - - def _connect_handler(self, data): - parsed = json.loads(data) - self.socket_id = parsed['socket_id'] - self.state = "connected" - - def _failed_handler(self, data): - self.state = "failed" - - def _ping_handler(self, data): - self.send_pong() - # Restart our timers since we received something on the connection - self._start_timers() - - def _pong_handler(self, data): - self.logger.info("Connection: pong from pusher") - self.pong_received = True - - def _pusher_error_handler(self, data): - if 'code' in data: - error_code = None - - try: - error_code = int(data['code']) - except: - pass - - if error_code is not None: - self.logger.error("Connection: Received error %s" % error_code) - - if (error_code >= 4000) and (error_code <= 4099): - # The connection SHOULD NOT be re-established unchanged - self.logger.info("Connection: Error is unrecoverable. Disconnecting") - self.disconnect() - elif (error_code >= 4100) and (error_code <= 4199): - # The connection SHOULD be re-established after backing off - self.reconnect() - elif (error_code >= 4200) and (error_code <= 4299): - # The connection SHOULD be re-established immediately - self.reconnect(0) - else: - pass - else: - self.logger.error("Connection: Unknown error code") - else: - self.logger.error("Connection: No error code supplied") - - def _connection_timed_out(self): - self.logger.info("Did not receive any data in time. Reconnecting.") - self.state = "failed" - self.reconnect() +from threading import Thread, Timer +import websocket +import logging +import time + +try: + import simplejson as json +except ImportError: + import json + + +class Connection(Thread): + def __init__(self, event_handler, url, log_level=logging.INFO, daemon=True, reconnect_interval=10): + self.event_handler = event_handler + self.url = url + + self.socket = None + self.socket_id = "" + + self.event_callbacks = {} + + self.disconnect_called = False + self.needs_reconnect = False + self.default_reconnect_interval = reconnect_interval + self.reconnect_interval = reconnect_interval + + self.pong_timer = None + self.pong_received = False + self.pong_timeout = 30 + + self.bind("pusher:connection_established", self._connect_handler) + self.bind("pusher:connection_failed", self._failed_handler) + self.bind("pusher:pong", self._pong_handler) + self.bind("pusher:ping", self._ping_handler) + self.bind("pusher:error", self._pusher_error_handler) + + self.state = "initialized" + + self.logger = logging.getLogger(self.__module__) # create a new logger + if log_level == logging.DEBUG: + websocket.enableTrace(True) + self.logger.setLevel(log_level) + + # From Martyn's comment at: + # https://pusher.tenderapp.com/discussions/problems/36-no-messages-received-after-1-idle-minute-heartbeat + # "We send a ping every 5 minutes in an attempt to keep connections + # alive..." + # This is why we set the connection timeout to 5 minutes, since we can + # expect a pusher heartbeat message every 5 minutes. Adding 5 sec to + # account for small timing delays which may cause messages to not be + # received in exact 5 minute intervals. + + self.connection_timeout = 305 + self.connection_timer = None + + self.ping_interval = 120 + self.ping_timer = None + + Thread.__init__(self) + self.daemon = daemon + + def bind(self, event_name, callback, kwargs={}): + """Bind an event to a callback + + :param event_name: The name of the event to bind to. + :type event_name: str + + :param callback: The callback to notify of this event. + """ + + if event_name not in self.event_callbacks.keys(): + self.event_callbacks[event_name] = [] + + self.event_callbacks[event_name].append({"func": callback, + "kwargs": kwargs}) + + def disconnect(self): + self.needs_reconnect = False + self.disconnect_called = True + if self.socket: + self.socket.close() + self.join() + + def reconnect(self, reconnect_interval=None): + if reconnect_interval is None: + reconnect_interval = self.default_reconnect_interval + + self.logger.info("Connection: Reconnect in %s" % reconnect_interval) + self.reconnect_interval = reconnect_interval + + self.needs_reconnect = True + if self.socket: + self.socket.close() + + def run(self): + self._connect() + + def _connect(self): + self.state = "connecting" + + self.socket = websocket.WebSocketApp( + self.url, + on_open=self._on_open, + on_message=self._on_message, + on_error=self._on_error, + on_close=self._on_close + ) + + self.socket.run_forever() + + while self.needs_reconnect and not self.disconnect_called: + self.logger.info("Attempting to connect again in %s seconds." + % self.reconnect_interval) + self.state = "unavailable" + time.sleep(self.reconnect_interval) + + # We need to set this flag since closing the socket will set it to + # false + self.socket.keep_running = True + self.socket.run_forever() + + def _on_open(self, ws): + self.logger.info("Connection: Connection opened") + # Send a ping right away to inform that the connection is alive. If you + # don't do this, it takes the ping interval to subcribe to channel and + # events + self.send_ping() + self._start_timers() + + def _on_error(self, ws, error): + self.logger.info("Connection: Error - %s" % error) + self.state = "failed" + self.needs_reconnect = True + + def _on_message(self, ws, message): + self.logger.info("Connection: Message - %s" % message) + + # Stop our timeout timer, since we got some data + self._stop_timers() + + params = self._parse(message) + + if 'event' in params.keys(): + if 'channel' not in params.keys(): + # We've got a connection event. Lets handle it. + if params['event'] in self.event_callbacks.keys(): + for callback in self.event_callbacks[params['event']]: + try: + callback["func"](params['data'], + **callback["kwargs"]) + except Exception: + self.logger.exception("Callback raised unhandled") + else: + self.logger.info("Connection: Unhandled event") + else: + # We've got a channel event. Lets pass it up to the pusher + # so it can be handled by the appropriate channel. + self.event_handler( + params['event'], + params['data'], + params['channel'] + ) + + # We've handled our data, so restart our connection timeout handler + self._start_timers() + + def _on_close(self, ws, *args): + self.logger.info("Connection: Connection closed") + self.state = "disconnected" + self._stop_timers() + + @staticmethod + def _parse(message): + return json.loads(message) + + def _stop_timers(self): + if self.ping_timer: + self.ping_timer.cancel() + + if self.connection_timer: + self.connection_timer.cancel() + + if self.pong_timer: + self.pong_timer.cancel() + + def _start_timers(self): + self._stop_timers() + + self.ping_timer = Timer(self.ping_interval, self.send_ping) + self.ping_timer.start() + + self.connection_timer = Timer(self.connection_timeout, self._connection_timed_out) + self.connection_timer.start() + + def send_event(self, event_name, data, channel_name=None): + event = {'event': event_name, 'data': data} + if channel_name: + event['channel'] = channel_name + + self.logger.info("Connection: Sending event - %s" % event) + try: + self.socket.send(json.dumps(event)) + except Exception as e: + self.logger.error("Failed send event: %s" % e) + + def send_ping(self): + self.logger.info("Connection: ping to pusher") + try: + self.socket.send(json.dumps({'event': 'pusher:ping', 'data': ''})) + except Exception as e: + self.logger.error("Failed send ping: %s" % e) + self.pong_timer = Timer(self.pong_timeout, self._check_pong) + self.pong_timer.start() + + def send_pong(self): + self.logger.info("Connection: pong to pusher") + try: + self.socket.send(json.dumps({'event': 'pusher:pong', 'data': ''})) + except Exception as e: + self.logger.error("Failed send pong: %s" % e) + + def _check_pong(self): + self.pong_timer.cancel() + + if self.pong_received: + self.pong_received = False + else: + self.logger.info("Did not receive pong in time. Will attempt to reconnect.") + self.state = "failed" + self.reconnect() + + def _connect_handler(self, data): + parsed = json.loads(data) + self.socket_id = parsed['socket_id'] + self.state = "connected" + + def _failed_handler(self, data): + self.state = "failed" + + def _ping_handler(self, data): + self.send_pong() + # Restart our timers since we received something on the connection + self._start_timers() + + def _pong_handler(self, data): + self.logger.info("Connection: pong from pusher") + self.pong_received = True + + def _pusher_error_handler(self, data): + if 'code' in data: + error_code = None + + try: + error_code = int(data['code']) + except: + pass + + if error_code is not None: + self.logger.error("Connection: Received error %s" % error_code) + + if (error_code >= 4000) and (error_code <= 4099): + # The connection SHOULD NOT be re-established unchanged + self.logger.info("Connection: Error is unrecoverable. Disconnecting") + self.disconnect() + elif (error_code >= 4100) and (error_code <= 4199): + # The connection SHOULD be re-established after backing off + self.reconnect() + elif (error_code >= 4200) and (error_code <= 4299): + # The connection SHOULD be re-established immediately + self.reconnect(0) + else: + pass + else: + self.logger.error("Connection: Unknown error code") + else: + self.logger.error("Connection: No error code supplied") + + def _connection_timed_out(self): + self.logger.info("Did not receive any data in time. Reconnecting.") + self.state = "failed" + self.reconnect() From afb766a6b76563db9305c9e054e7e0fb28f0cf0e Mon Sep 17 00:00:00 2001 From: bartbroere Date: Fri, 16 Sep 2016 10:22:43 +0200 Subject: [PATCH 04/10] Documentation update, code was tested --- pusherclient/channel.py | 3 +++ pusherclient/connection.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/pusherclient/channel.py b/pusherclient/channel.py index 8ad3b1a..a4f9d0a 100755 --- a/pusherclient/channel.py +++ b/pusherclient/channel.py @@ -13,6 +13,9 @@ def bind(self, event_name, callback, kwargs={}): :type event_name: str :param callback: The callback to notify of this event. + + :param kwargs: The keyword arguments to pass to the callback. + :type kwargs: dict """ if event_name not in self.event_callbacks.keys(): self.event_callbacks[event_name] = [] diff --git a/pusherclient/connection.py b/pusherclient/connection.py index 9a8c8f8..d8a7641 100755 --- a/pusherclient/connection.py +++ b/pusherclient/connection.py @@ -66,6 +66,9 @@ def bind(self, event_name, callback, kwargs={}): :type event_name: str :param callback: The callback to notify of this event. + + :param kwargs: The keyword arguments to pass to the callback. + :type kwargs: dict """ if event_name not in self.event_callbacks.keys(): From 6cdc719aca7ce9177c71c273bc9fb6d63e94a214 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Fri, 16 Sep 2016 15:47:57 +0200 Subject: [PATCH 05/10] Cleaning the diff hopefully --- pusherclient/channel.py | 86 +++--- pusherclient/connection.py | 568 ++++++++++++++++++------------------- 2 files changed, 327 insertions(+), 327 deletions(-) diff --git a/pusherclient/channel.py b/pusherclient/channel.py index a4f9d0a..26fb5d7 100755 --- a/pusherclient/channel.py +++ b/pusherclient/channel.py @@ -1,43 +1,43 @@ -class Channel(object): - def __init__(self, channel_name, connection): - self.name = channel_name - - self.connection = connection - - self.event_callbacks = {} - - def bind(self, event_name, callback, kwargs={}): - """Bind an event to a callback - - :param event_name: The name of the event to bind to. - :type event_name: str - - :param callback: The callback to notify of this event. - - :param kwargs: The keyword arguments to pass to the callback. - :type kwargs: dict - """ - if event_name not in self.event_callbacks.keys(): - self.event_callbacks[event_name] = [] - - self.event_callbacks[event_name].append({"func": callback, - "kwargs": kwargs}) - - def trigger(self, event_name, data): - """Trigger an event on this channel. Only available for private or - presence channels - - :param event_name: The name of the event. Must begin with 'client-'' - :type event_name: str - - :param data: The data to send with the event. - """ - if self.connection: - if event_name.startswith("client-"): - if self.name.startswith("private-") or self.name.startswith("presence-"): - self.connection.send_event(event_name, data, channel_name=self.name) - - def _handle_event(self, event_name, data): - if event_name in self.event_callbacks.keys(): - for callback in self.event_callbacks[event_name]: - callback["func"](data, **callback["kwargs"]) +class Channel(object): + def __init__(self, channel_name, connection): + self.name = channel_name + + self.connection = connection + + self.event_callbacks = {} + + def bind(self, event_name, callback, kwargs={}): + """Bind an event to a callback + + :param event_name: The name of the event to bind to. + :type event_name: str + + :param callback: The callback to notify of this event. + + :param kwargs: The keyword arguments to pass to the callback. + :type kwargs: dict + """ + if event_name not in self.event_callbacks.keys(): + self.event_callbacks[event_name] = [] + + self.event_callbacks[event_name].append({"func": callback, + "kwargs": kwargs}) + + def trigger(self, event_name, data): + """Trigger an event on this channel. Only available for private or + presence channels + + :param event_name: The name of the event. Must begin with 'client-'' + :type event_name: str + + :param data: The data to send with the event. + """ + if self.connection: + if event_name.startswith("client-"): + if self.name.startswith("private-") or self.name.startswith("presence-"): + self.connection.send_event(event_name, data, channel_name=self.name) + + def _handle_event(self, event_name, data): + if event_name in self.event_callbacks.keys(): + for callback in self.event_callbacks[event_name]: + callback["func"](data, **callback["kwargs"]) diff --git a/pusherclient/connection.py b/pusherclient/connection.py index d8a7641..78d5ddf 100755 --- a/pusherclient/connection.py +++ b/pusherclient/connection.py @@ -1,284 +1,284 @@ -from threading import Thread, Timer -import websocket -import logging -import time - -try: - import simplejson as json -except ImportError: - import json - - -class Connection(Thread): - def __init__(self, event_handler, url, log_level=logging.INFO, daemon=True, reconnect_interval=10): - self.event_handler = event_handler - self.url = url - - self.socket = None - self.socket_id = "" - - self.event_callbacks = {} - - self.disconnect_called = False - self.needs_reconnect = False - self.default_reconnect_interval = reconnect_interval - self.reconnect_interval = reconnect_interval - - self.pong_timer = None - self.pong_received = False - self.pong_timeout = 30 - - self.bind("pusher:connection_established", self._connect_handler) - self.bind("pusher:connection_failed", self._failed_handler) - self.bind("pusher:pong", self._pong_handler) - self.bind("pusher:ping", self._ping_handler) - self.bind("pusher:error", self._pusher_error_handler) - - self.state = "initialized" - - self.logger = logging.getLogger(self.__module__) # create a new logger - if log_level == logging.DEBUG: - websocket.enableTrace(True) - self.logger.setLevel(log_level) - - # From Martyn's comment at: - # https://pusher.tenderapp.com/discussions/problems/36-no-messages-received-after-1-idle-minute-heartbeat - # "We send a ping every 5 minutes in an attempt to keep connections - # alive..." - # This is why we set the connection timeout to 5 minutes, since we can - # expect a pusher heartbeat message every 5 minutes. Adding 5 sec to - # account for small timing delays which may cause messages to not be - # received in exact 5 minute intervals. - - self.connection_timeout = 305 - self.connection_timer = None - - self.ping_interval = 120 - self.ping_timer = None - - Thread.__init__(self) - self.daemon = daemon - - def bind(self, event_name, callback, kwargs={}): - """Bind an event to a callback - - :param event_name: The name of the event to bind to. - :type event_name: str - - :param callback: The callback to notify of this event. - - :param kwargs: The keyword arguments to pass to the callback. - :type kwargs: dict - """ - - if event_name not in self.event_callbacks.keys(): - self.event_callbacks[event_name] = [] - - self.event_callbacks[event_name].append({"func": callback, - "kwargs": kwargs}) - - def disconnect(self): - self.needs_reconnect = False - self.disconnect_called = True - if self.socket: - self.socket.close() - self.join() - - def reconnect(self, reconnect_interval=None): - if reconnect_interval is None: - reconnect_interval = self.default_reconnect_interval - - self.logger.info("Connection: Reconnect in %s" % reconnect_interval) - self.reconnect_interval = reconnect_interval - - self.needs_reconnect = True - if self.socket: - self.socket.close() - - def run(self): - self._connect() - - def _connect(self): - self.state = "connecting" - - self.socket = websocket.WebSocketApp( - self.url, - on_open=self._on_open, - on_message=self._on_message, - on_error=self._on_error, - on_close=self._on_close - ) - - self.socket.run_forever() - - while self.needs_reconnect and not self.disconnect_called: - self.logger.info("Attempting to connect again in %s seconds." - % self.reconnect_interval) - self.state = "unavailable" - time.sleep(self.reconnect_interval) - - # We need to set this flag since closing the socket will set it to - # false - self.socket.keep_running = True - self.socket.run_forever() - - def _on_open(self, ws): - self.logger.info("Connection: Connection opened") - # Send a ping right away to inform that the connection is alive. If you - # don't do this, it takes the ping interval to subcribe to channel and - # events - self.send_ping() - self._start_timers() - - def _on_error(self, ws, error): - self.logger.info("Connection: Error - %s" % error) - self.state = "failed" - self.needs_reconnect = True - - def _on_message(self, ws, message): - self.logger.info("Connection: Message - %s" % message) - - # Stop our timeout timer, since we got some data - self._stop_timers() - - params = self._parse(message) - - if 'event' in params.keys(): - if 'channel' not in params.keys(): - # We've got a connection event. Lets handle it. - if params['event'] in self.event_callbacks.keys(): - for callback in self.event_callbacks[params['event']]: - try: - callback["func"](params['data'], - **callback["kwargs"]) - except Exception: - self.logger.exception("Callback raised unhandled") - else: - self.logger.info("Connection: Unhandled event") - else: - # We've got a channel event. Lets pass it up to the pusher - # so it can be handled by the appropriate channel. - self.event_handler( - params['event'], - params['data'], - params['channel'] - ) - - # We've handled our data, so restart our connection timeout handler - self._start_timers() - - def _on_close(self, ws, *args): - self.logger.info("Connection: Connection closed") - self.state = "disconnected" - self._stop_timers() - - @staticmethod - def _parse(message): - return json.loads(message) - - def _stop_timers(self): - if self.ping_timer: - self.ping_timer.cancel() - - if self.connection_timer: - self.connection_timer.cancel() - - if self.pong_timer: - self.pong_timer.cancel() - - def _start_timers(self): - self._stop_timers() - - self.ping_timer = Timer(self.ping_interval, self.send_ping) - self.ping_timer.start() - - self.connection_timer = Timer(self.connection_timeout, self._connection_timed_out) - self.connection_timer.start() - - def send_event(self, event_name, data, channel_name=None): - event = {'event': event_name, 'data': data} - if channel_name: - event['channel'] = channel_name - - self.logger.info("Connection: Sending event - %s" % event) - try: - self.socket.send(json.dumps(event)) - except Exception as e: - self.logger.error("Failed send event: %s" % e) - - def send_ping(self): - self.logger.info("Connection: ping to pusher") - try: - self.socket.send(json.dumps({'event': 'pusher:ping', 'data': ''})) - except Exception as e: - self.logger.error("Failed send ping: %s" % e) - self.pong_timer = Timer(self.pong_timeout, self._check_pong) - self.pong_timer.start() - - def send_pong(self): - self.logger.info("Connection: pong to pusher") - try: - self.socket.send(json.dumps({'event': 'pusher:pong', 'data': ''})) - except Exception as e: - self.logger.error("Failed send pong: %s" % e) - - def _check_pong(self): - self.pong_timer.cancel() - - if self.pong_received: - self.pong_received = False - else: - self.logger.info("Did not receive pong in time. Will attempt to reconnect.") - self.state = "failed" - self.reconnect() - - def _connect_handler(self, data): - parsed = json.loads(data) - self.socket_id = parsed['socket_id'] - self.state = "connected" - - def _failed_handler(self, data): - self.state = "failed" - - def _ping_handler(self, data): - self.send_pong() - # Restart our timers since we received something on the connection - self._start_timers() - - def _pong_handler(self, data): - self.logger.info("Connection: pong from pusher") - self.pong_received = True - - def _pusher_error_handler(self, data): - if 'code' in data: - error_code = None - - try: - error_code = int(data['code']) - except: - pass - - if error_code is not None: - self.logger.error("Connection: Received error %s" % error_code) - - if (error_code >= 4000) and (error_code <= 4099): - # The connection SHOULD NOT be re-established unchanged - self.logger.info("Connection: Error is unrecoverable. Disconnecting") - self.disconnect() - elif (error_code >= 4100) and (error_code <= 4199): - # The connection SHOULD be re-established after backing off - self.reconnect() - elif (error_code >= 4200) and (error_code <= 4299): - # The connection SHOULD be re-established immediately - self.reconnect(0) - else: - pass - else: - self.logger.error("Connection: Unknown error code") - else: - self.logger.error("Connection: No error code supplied") - - def _connection_timed_out(self): - self.logger.info("Did not receive any data in time. Reconnecting.") - self.state = "failed" - self.reconnect() +from threading import Thread, Timer +import websocket +import logging +import time + +try: + import simplejson as json +except ImportError: + import json + + +class Connection(Thread): + def __init__(self, event_handler, url, log_level=logging.INFO, daemon=True, reconnect_interval=10): + self.event_handler = event_handler + self.url = url + + self.socket = None + self.socket_id = "" + + self.event_callbacks = {} + + self.disconnect_called = False + self.needs_reconnect = False + self.default_reconnect_interval = reconnect_interval + self.reconnect_interval = reconnect_interval + + self.pong_timer = None + self.pong_received = False + self.pong_timeout = 30 + + self.bind("pusher:connection_established", self._connect_handler) + self.bind("pusher:connection_failed", self._failed_handler) + self.bind("pusher:pong", self._pong_handler) + self.bind("pusher:ping", self._ping_handler) + self.bind("pusher:error", self._pusher_error_handler) + + self.state = "initialized" + + self.logger = logging.getLogger(self.__module__) # create a new logger + if log_level == logging.DEBUG: + websocket.enableTrace(True) + self.logger.setLevel(log_level) + + # From Martyn's comment at: + # https://pusher.tenderapp.com/discussions/problems/36-no-messages-received-after-1-idle-minute-heartbeat + # "We send a ping every 5 minutes in an attempt to keep connections + # alive..." + # This is why we set the connection timeout to 5 minutes, since we can + # expect a pusher heartbeat message every 5 minutes. Adding 5 sec to + # account for small timing delays which may cause messages to not be + # received in exact 5 minute intervals. + + self.connection_timeout = 305 + self.connection_timer = None + + self.ping_interval = 120 + self.ping_timer = None + + Thread.__init__(self) + self.daemon = daemon + + def bind(self, event_name, callback, kwargs={}): + """Bind an event to a callback + + :param event_name: The name of the event to bind to. + :type event_name: str + + :param callback: The callback to notify of this event. + + :param kwargs: The keyword arguments to pass to the callback. + :type kwargs: dict + """ + + if event_name not in self.event_callbacks.keys(): + self.event_callbacks[event_name] = [] + + self.event_callbacks[event_name].append({"func": callback, + "kwargs": kwargs}) + + def disconnect(self): + self.needs_reconnect = False + self.disconnect_called = True + if self.socket: + self.socket.close() + self.join() + + def reconnect(self, reconnect_interval=None): + if reconnect_interval is None: + reconnect_interval = self.default_reconnect_interval + + self.logger.info("Connection: Reconnect in %s" % reconnect_interval) + self.reconnect_interval = reconnect_interval + + self.needs_reconnect = True + if self.socket: + self.socket.close() + + def run(self): + self._connect() + + def _connect(self): + self.state = "connecting" + + self.socket = websocket.WebSocketApp( + self.url, + on_open=self._on_open, + on_message=self._on_message, + on_error=self._on_error, + on_close=self._on_close + ) + + self.socket.run_forever() + + while self.needs_reconnect and not self.disconnect_called: + self.logger.info("Attempting to connect again in %s seconds." + % self.reconnect_interval) + self.state = "unavailable" + time.sleep(self.reconnect_interval) + + # We need to set this flag since closing the socket will set it to + # false + self.socket.keep_running = True + self.socket.run_forever() + + def _on_open(self, ws): + self.logger.info("Connection: Connection opened") + # Send a ping right away to inform that the connection is alive. If you + # don't do this, it takes the ping interval to subcribe to channel and + # events + self.send_ping() + self._start_timers() + + def _on_error(self, ws, error): + self.logger.info("Connection: Error - %s" % error) + self.state = "failed" + self.needs_reconnect = True + + def _on_message(self, ws, message): + self.logger.info("Connection: Message - %s" % message) + + # Stop our timeout timer, since we got some data + self._stop_timers() + + params = self._parse(message) + + if 'event' in params.keys(): + if 'channel' not in params.keys(): + # We've got a connection event. Lets handle it. + if params['event'] in self.event_callbacks.keys(): + for callback in self.event_callbacks[params['event']]: + try: + callback["func"](params['data'], + **callback["kwargs"]) + except Exception: + self.logger.exception("Callback raised unhandled") + else: + self.logger.info("Connection: Unhandled event") + else: + # We've got a channel event. Lets pass it up to the pusher + # so it can be handled by the appropriate channel. + self.event_handler( + params['event'], + params['data'], + params['channel'] + ) + + # We've handled our data, so restart our connection timeout handler + self._start_timers() + + def _on_close(self, ws, *args): + self.logger.info("Connection: Connection closed") + self.state = "disconnected" + self._stop_timers() + + @staticmethod + def _parse(message): + return json.loads(message) + + def _stop_timers(self): + if self.ping_timer: + self.ping_timer.cancel() + + if self.connection_timer: + self.connection_timer.cancel() + + if self.pong_timer: + self.pong_timer.cancel() + + def _start_timers(self): + self._stop_timers() + + self.ping_timer = Timer(self.ping_interval, self.send_ping) + self.ping_timer.start() + + self.connection_timer = Timer(self.connection_timeout, self._connection_timed_out) + self.connection_timer.start() + + def send_event(self, event_name, data, channel_name=None): + event = {'event': event_name, 'data': data} + if channel_name: + event['channel'] = channel_name + + self.logger.info("Connection: Sending event - %s" % event) + try: + self.socket.send(json.dumps(event)) + except Exception as e: + self.logger.error("Failed send event: %s" % e) + + def send_ping(self): + self.logger.info("Connection: ping to pusher") + try: + self.socket.send(json.dumps({'event': 'pusher:ping', 'data': ''})) + except Exception as e: + self.logger.error("Failed send ping: %s" % e) + self.pong_timer = Timer(self.pong_timeout, self._check_pong) + self.pong_timer.start() + + def send_pong(self): + self.logger.info("Connection: pong to pusher") + try: + self.socket.send(json.dumps({'event': 'pusher:pong', 'data': ''})) + except Exception as e: + self.logger.error("Failed send pong: %s" % e) + + def _check_pong(self): + self.pong_timer.cancel() + + if self.pong_received: + self.pong_received = False + else: + self.logger.info("Did not receive pong in time. Will attempt to reconnect.") + self.state = "failed" + self.reconnect() + + def _connect_handler(self, data): + parsed = json.loads(data) + self.socket_id = parsed['socket_id'] + self.state = "connected" + + def _failed_handler(self, data): + self.state = "failed" + + def _ping_handler(self, data): + self.send_pong() + # Restart our timers since we received something on the connection + self._start_timers() + + def _pong_handler(self, data): + self.logger.info("Connection: pong from pusher") + self.pong_received = True + + def _pusher_error_handler(self, data): + if 'code' in data: + error_code = None + + try: + error_code = int(data['code']) + except: + pass + + if error_code is not None: + self.logger.error("Connection: Received error %s" % error_code) + + if (error_code >= 4000) and (error_code <= 4099): + # The connection SHOULD NOT be re-established unchanged + self.logger.info("Connection: Error is unrecoverable. Disconnecting") + self.disconnect() + elif (error_code >= 4100) and (error_code <= 4199): + # The connection SHOULD be re-established after backing off + self.reconnect() + elif (error_code >= 4200) and (error_code <= 4299): + # The connection SHOULD be re-established immediately + self.reconnect(0) + else: + pass + else: + self.logger.error("Connection: Unknown error code") + else: + self.logger.error("Connection: No error code supplied") + + def _connection_timed_out(self): + self.logger.info("Did not receive any data in time. Reconnecting.") + self.state = "failed" + self.reconnect() From d206d56d2ffe13e032fab0a7117acef4d1529ce0 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Fri, 16 Sep 2016 14:53:01 +0200 Subject: [PATCH 06/10] No longer test for Python 2.6, twisted does not support this --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 04b6215..0508b7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: python python: - - "2.6" + # Disable testing 2.6 since twisted has Python 2.7 or higher as a dependency. + # https://twistedmatrix.com/pipermail/twisted-python/2015-March/029270.html +# - "2.6" - "2.7" # Disable testing 3.2 since autobahn uses unicode literal syntax that wasn't # re-added into 3.3. See PEP 414 From c48fe8536b6b9227652e416fecd73de5d6510fc6 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Mon, 19 Sep 2016 11:47:16 +0200 Subject: [PATCH 07/10] Insert json decoding as option in bind callback --- .travis.yml | 42 +++++++++++++++++++------------------- pusherclient/channel.py | 15 +++++++++++--- pusherclient/connection.py | 15 ++++++++++---- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0508b7f..4a735b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,21 @@ -language: python -python: - # Disable testing 2.6 since twisted has Python 2.7 or higher as a dependency. - # https://twistedmatrix.com/pipermail/twisted-python/2015-March/029270.html -# - "2.6" - - "2.7" - # Disable testing 3.2 since autobahn uses unicode literal syntax that wasn't - # re-added into 3.3. See PEP 414 -# - "3.2" - - "3.3" - - "3.4" -install: - - pip install . - # Our test server uses autobahn - - pip install autobahn - # For 2.x, our server needs twisted - - pip install twisted - # For 3.x where x < 4, our server needs trollius - - pip install trollius -script: python tests/test_pusherclient.py - +language: python +python: + # Disable testing 2.6 since twisted has Python 2.7 or higher as a dependency. + # https://twistedmatrix.com/pipermail/twisted-python/2015-March/029270.html +# - "2.6" + - "2.7" + # Disable testing 3.2 since autobahn uses unicode literal syntax that wasn't + # re-added into 3.3. See PEP 414 +# - "3.2" + - "3.3" + - "3.4" +install: + - pip install . + # Our test server uses autobahn + - pip install autobahn + # For 2.x, our server needs twisted + - pip install twisted + # For 3.x where x < 4, our server needs trollius + - pip install trollius +script: python tests/test_pusherclient.py + diff --git a/pusherclient/channel.py b/pusherclient/channel.py index 26fb5d7..b40fef4 100755 --- a/pusherclient/channel.py +++ b/pusherclient/channel.py @@ -1,3 +1,8 @@ +try: + import simplejson as json +except ImportError: + import json + class Channel(object): def __init__(self, channel_name, connection): self.name = channel_name @@ -6,7 +11,7 @@ def __init__(self, channel_name, connection): self.event_callbacks = {} - def bind(self, event_name, callback, kwargs={}): + def bind(self, event_name, callback, kwargs={}, decode_json=False): """Bind an event to a callback :param event_name: The name of the event to bind to. @@ -21,7 +26,8 @@ def bind(self, event_name, callback, kwargs={}): self.event_callbacks[event_name] = [] self.event_callbacks[event_name].append({"func": callback, - "kwargs": kwargs}) + "kwargs": kwargs, + "decode_json": decode_json}) def trigger(self, event_name, data): """Trigger an event on this channel. Only available for private or @@ -40,4 +46,7 @@ def trigger(self, event_name, data): def _handle_event(self, event_name, data): if event_name in self.event_callbacks.keys(): for callback in self.event_callbacks[event_name]: - callback["func"](data, **callback["kwargs"]) + if callback["decode_json"]: + callback["func"](json.loads(data), **callback["kwargs"]) + else: + callback["func"](data, **callback["kwargs"]) diff --git a/pusherclient/connection.py b/pusherclient/connection.py index 78d5ddf..ceb5bdb 100755 --- a/pusherclient/connection.py +++ b/pusherclient/connection.py @@ -59,7 +59,9 @@ def __init__(self, event_handler, url, log_level=logging.INFO, daemon=True, reco Thread.__init__(self) self.daemon = daemon - def bind(self, event_name, callback, kwargs={}): + #TODO add an option to decode json of the message, to make implementations + #more DRY + def bind(self, event_name, callback, kwargs={}, decode_json=False): """Bind an event to a callback :param event_name: The name of the event to bind to. @@ -75,7 +77,8 @@ def bind(self, event_name, callback, kwargs={}): self.event_callbacks[event_name] = [] self.event_callbacks[event_name].append({"func": callback, - "kwargs": kwargs}) + "kwargs": kwargs, + "decode_json": decode_json}) def disconnect(self): self.needs_reconnect = False @@ -149,8 +152,12 @@ def _on_message(self, ws, message): if params['event'] in self.event_callbacks.keys(): for callback in self.event_callbacks[params['event']]: try: - callback["func"](params['data'], - **callback["kwargs"]) + if callback["decode_json"]: + callback["func"](json.loads(params['data']), + **callback["kwargs"]) + else: + callback["func"](params['data'], + **callback["kwargs"]) except Exception: self.logger.exception("Callback raised unhandled") else: From 3ec894568957e161c19b81564453e195eeec6280 Mon Sep 17 00:00:00 2001 From: bartbroere Date: Sat, 19 Nov 2016 20:57:17 +0100 Subject: [PATCH 08/10] Documentation about decode_json --- pusherclient/channel.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pusherclient/channel.py b/pusherclient/channel.py index b40fef4..b431afe 100755 --- a/pusherclient/channel.py +++ b/pusherclient/channel.py @@ -21,6 +21,10 @@ def bind(self, event_name, callback, kwargs={}, decode_json=False): :param kwargs: The keyword arguments to pass to the callback. :type kwargs: dict + + :param decode_json: Boolean that determines whether json messages will + be sent to the callback in decoded form + :type decode_json: boolean """ if event_name not in self.event_callbacks.keys(): self.event_callbacks[event_name] = [] From c41a77151e652cccb7f14d2f4f1936cf0d26ed54 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Thu, 24 Nov 2016 20:34:19 +0100 Subject: [PATCH 09/10] Global rename --- LICENSE | 2 +- README.md | 8 ++++---- examples/basic.py | 4 ++-- examples/presence.py | 4 ++-- examples/private.py | 4 ++-- {pusherclient => pusherclientb}/__init__.py | 0 {pusherclient => pusherclientb}/channel.py | 0 {pusherclient => pusherclientb}/connection.py | 0 setup.py | 12 ++++++------ 9 files changed, 17 insertions(+), 17 deletions(-) rename {pusherclient => pusherclientb}/__init__.py (100%) mode change 100755 => 100644 rename {pusherclient => pusherclientb}/channel.py (100%) mode change 100755 => 100644 rename {pusherclient => pusherclientb}/connection.py (100%) mode change 100755 => 100644 diff --git a/LICENSE b/LICENSE index 0cda525..37def3b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011 Erik Kulyk +Copyright (c) 2016 Bart Broere Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 34e5bee..375e9b7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -[![Build Status](https://travis-ci.org/ekulyk/PythonPusherClient.svg?branch=master)](https://travis-ci.org/ekulyk/PythonPusherClient) +[![Build Status](https://travis-ci.org/bartbroere/PythonPusherClient.svg?branch=master)](https://travis-ci.org/bartbroere/PythonPusherClient) -pusherclient +pusherclientb ============= -pusherclient is a python module for handling pusher websockets +pusherclientb is a python module for handling pusher websockets. This is a branched off version of the original (pusherclient) by Erik Kulyk. It is fully backwards compatible, and in addition provides decoded messages and custom arguments in the callback. Installation ------------ @@ -60,5 +60,5 @@ The ruby gem by Logan Koester which provides a similar service was also very hel Copyright --------- -MTI License - See LICENSE for details. +MIT License - See LICENSE for details. diff --git a/examples/basic.py b/examples/basic.py index ed09757..68f0172 100755 --- a/examples/basic.py +++ b/examples/basic.py @@ -5,7 +5,7 @@ import time -import pusherclient +import pusherclientb # Add a logging handler so we can see the raw communication data import logging @@ -35,7 +35,7 @@ def connect_handler(data): appkey = sys.argv[1] - pusher = pusherclient.Pusher(appkey) + pusher = pusherclientb.Pusher(appkey) pusher.connection.bind('pusher:connection_established', connect_handler) pusher.connect() diff --git a/examples/presence.py b/examples/presence.py index 68e4d26..142114f 100755 --- a/examples/presence.py +++ b/examples/presence.py @@ -5,7 +5,7 @@ import time -import pusherclient +import pusherclientb global pusher @@ -30,7 +30,7 @@ def connect_handler(data): secret = sys.argv[2] userid = sys.argv[3] - pusher = pusherclient.Pusher(appkey, secret=secret, user_data={'user_id': userid}) + pusher = pusherclientb.Pusher(appkey, secret=secret, user_data={'user_id': userid}) pusher.connection.bind('pusher:connection_established', connect_handler) pusher.connect() diff --git a/examples/private.py b/examples/private.py index cab5cf2..1131cba 100755 --- a/examples/private.py +++ b/examples/private.py @@ -5,7 +5,7 @@ import time -import pusherclient +import pusherclientb global pusher @@ -29,7 +29,7 @@ def connect_handler(data): appkey = sys.argv[1] secret = sys.argv[2] - pusher = pusherclient.Pusher(appkey, secret=secret) + pusher = pusherclientb.Pusher(appkey, secret=secret) pusher.connection.bind('pusher:connection_established', connect_handler) pusher.connect() diff --git a/pusherclient/__init__.py b/pusherclientb/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from pusherclient/__init__.py rename to pusherclientb/__init__.py diff --git a/pusherclient/channel.py b/pusherclientb/channel.py old mode 100755 new mode 100644 similarity index 100% rename from pusherclient/channel.py rename to pusherclientb/channel.py diff --git a/pusherclient/connection.py b/pusherclientb/connection.py old mode 100755 new mode 100644 similarity index 100% rename from pusherclient/connection.py rename to pusherclientb/connection.py diff --git a/setup.py b/setup.py index 8d0fefa..6c1cb67 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup -VERSION = "0.3.0" +VERSION = "0.3.1" requirements = ["websocket-client"] @@ -9,17 +9,17 @@ def readme(): return f.read() setup( - name="pusherclient", + name="pusherclientb", version=VERSION, description="Pusher websocket client for python", long_description=readme(), keywords="pusher websocket client", - author="Erik Kulyk", - author_email="e.kulyk@gmail.com", + author="Bart Broere", + author_email="mail@bartbroere.eu", license="MIT", - url="https://github.com/ekulyk/PythonPusherClient", + url="https://github.com/bartbroere/PythonPusherClient", install_requires=requirements, - packages=["pusherclient"], + packages=["pusherclientb"], classifiers=[ 'Development Status :: 3 - Alpha', 'Environment :: Web Environment', From 8b43dd58956ee4e3f30d90d70b52b729c4cf3f0d Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Thu, 24 Nov 2016 20:34:19 +0100 Subject: [PATCH 10/10] Revert "Global rename" This reverts commit c41a77151e652cccb7f14d2f4f1936cf0d26ed54. --- LICENSE | 2 +- README.md | 8 ++++---- examples/basic.py | 4 ++-- examples/presence.py | 4 ++-- examples/private.py | 4 ++-- {pusherclientb => pusherclient}/__init__.py | 0 {pusherclientb => pusherclient}/channel.py | 0 {pusherclientb => pusherclient}/connection.py | 0 setup.py | 12 ++++++------ 9 files changed, 17 insertions(+), 17 deletions(-) rename {pusherclientb => pusherclient}/__init__.py (100%) mode change 100644 => 100755 rename {pusherclientb => pusherclient}/channel.py (100%) mode change 100644 => 100755 rename {pusherclientb => pusherclient}/connection.py (100%) mode change 100644 => 100755 diff --git a/LICENSE b/LICENSE index 37def3b..0cda525 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016 Bart Broere +Copyright (c) 2011 Erik Kulyk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 375e9b7..34e5bee 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -[![Build Status](https://travis-ci.org/bartbroere/PythonPusherClient.svg?branch=master)](https://travis-ci.org/bartbroere/PythonPusherClient) +[![Build Status](https://travis-ci.org/ekulyk/PythonPusherClient.svg?branch=master)](https://travis-ci.org/ekulyk/PythonPusherClient) -pusherclientb +pusherclient ============= -pusherclientb is a python module for handling pusher websockets. This is a branched off version of the original (pusherclient) by Erik Kulyk. It is fully backwards compatible, and in addition provides decoded messages and custom arguments in the callback. +pusherclient is a python module for handling pusher websockets Installation ------------ @@ -60,5 +60,5 @@ The ruby gem by Logan Koester which provides a similar service was also very hel Copyright --------- -MIT License - See LICENSE for details. +MTI License - See LICENSE for details. diff --git a/examples/basic.py b/examples/basic.py index 68f0172..ed09757 100755 --- a/examples/basic.py +++ b/examples/basic.py @@ -5,7 +5,7 @@ import time -import pusherclientb +import pusherclient # Add a logging handler so we can see the raw communication data import logging @@ -35,7 +35,7 @@ def connect_handler(data): appkey = sys.argv[1] - pusher = pusherclientb.Pusher(appkey) + pusher = pusherclient.Pusher(appkey) pusher.connection.bind('pusher:connection_established', connect_handler) pusher.connect() diff --git a/examples/presence.py b/examples/presence.py index 142114f..68e4d26 100755 --- a/examples/presence.py +++ b/examples/presence.py @@ -5,7 +5,7 @@ import time -import pusherclientb +import pusherclient global pusher @@ -30,7 +30,7 @@ def connect_handler(data): secret = sys.argv[2] userid = sys.argv[3] - pusher = pusherclientb.Pusher(appkey, secret=secret, user_data={'user_id': userid}) + pusher = pusherclient.Pusher(appkey, secret=secret, user_data={'user_id': userid}) pusher.connection.bind('pusher:connection_established', connect_handler) pusher.connect() diff --git a/examples/private.py b/examples/private.py index 1131cba..cab5cf2 100755 --- a/examples/private.py +++ b/examples/private.py @@ -5,7 +5,7 @@ import time -import pusherclientb +import pusherclient global pusher @@ -29,7 +29,7 @@ def connect_handler(data): appkey = sys.argv[1] secret = sys.argv[2] - pusher = pusherclientb.Pusher(appkey, secret=secret) + pusher = pusherclient.Pusher(appkey, secret=secret) pusher.connection.bind('pusher:connection_established', connect_handler) pusher.connect() diff --git a/pusherclientb/__init__.py b/pusherclient/__init__.py old mode 100644 new mode 100755 similarity index 100% rename from pusherclientb/__init__.py rename to pusherclient/__init__.py diff --git a/pusherclientb/channel.py b/pusherclient/channel.py old mode 100644 new mode 100755 similarity index 100% rename from pusherclientb/channel.py rename to pusherclient/channel.py diff --git a/pusherclientb/connection.py b/pusherclient/connection.py old mode 100644 new mode 100755 similarity index 100% rename from pusherclientb/connection.py rename to pusherclient/connection.py diff --git a/setup.py b/setup.py index 6c1cb67..8d0fefa 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup -VERSION = "0.3.1" +VERSION = "0.3.0" requirements = ["websocket-client"] @@ -9,17 +9,17 @@ def readme(): return f.read() setup( - name="pusherclientb", + name="pusherclient", version=VERSION, description="Pusher websocket client for python", long_description=readme(), keywords="pusher websocket client", - author="Bart Broere", - author_email="mail@bartbroere.eu", + author="Erik Kulyk", + author_email="e.kulyk@gmail.com", license="MIT", - url="https://github.com/bartbroere/PythonPusherClient", + url="https://github.com/ekulyk/PythonPusherClient", install_requires=requirements, - packages=["pusherclientb"], + packages=["pusherclient"], classifiers=[ 'Development Status :: 3 - Alpha', 'Environment :: Web Environment',