Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-websocket-client for 
openSUSE:Factory checked in at 2023-05-11 12:31:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-websocket-client (Old)
 and      /work/SRC/openSUSE:Factory/.python-websocket-client.new.1533 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-websocket-client"

Thu May 11 12:31:35 2023 rev:21 rq:1085984 version:1.5.1

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-websocket-client/python-websocket-client.changes
  2023-04-25 16:53:24.674146958 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-websocket-client.new.1533/python-websocket-client.changes
        2023-05-11 12:31:41.162172237 +0200
@@ -1,0 +2,12 @@
+Wed May 10 07:00:29 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 1.5.1:
+  * Fix logic bug that can cause disconnects
+  * Refactor and improve ping/pong logic to resolve several
+    issues, including an infinite loop issue during reconnect
+  * Fix issue where `skip_utf8_validation = True` is ignored
+  * Fix issue where sslopt `is_ssl` is ignored
+  * Downgrade "websocket connected" message from logging.warning
+    to logging.info
+
+-------------------------------------------------------------------

Old:
----
  websocket-client-1.4.2.tar.gz

New:
----
  websocket-client-1.5.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-websocket-client.spec ++++++
--- /var/tmp/diff_new_pack.8A70VM/_old  2023-05-11 12:31:41.670174734 +0200
+++ /var/tmp/diff_new_pack.8A70VM/_new  2023-05-11 12:31:41.674174753 +0200
@@ -22,11 +22,9 @@
 %bcond_with libalternatives
 %endif
 
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
-%define skip_python2 1
 %{?sle15_python_module_pythons}
 Name:           python-websocket-client
-Version:        1.4.2
+Version:        1.5.1
 Release:        0
 Summary:        WebSocket client implementation
 License:        LGPL-2.1-only

++++++ websocket-client-1.4.2.tar.gz -> websocket-client-1.5.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/websocket-client-1.4.2/ChangeLog 
new/websocket-client-1.5.1/ChangeLog
--- old/websocket-client-1.4.2/ChangeLog        2022-11-04 05:01:44.000000000 
+0100
+++ new/websocket-client-1.5.1/ChangeLog        2023-02-04 18:55:28.000000000 
+0100
@@ -1,6 +1,16 @@
 ChangeLog
 ============
 
+- 1.5.1
+  - Fix logic bug that can cause disconnects (#893)
+
+- 1.5.0
+  - Refactor and improve ping/pong logic to resolve several issues, including 
an infinite loop issue during reconnect (#862)
+  - Fix issue where `skip_utf8_validation = True` is ignored (#886)
+  - Fix issue where sslopt `is_ssl` is ignored (#875)
+  - Downgrade "websocket connected" message from logging.warning to 
logging.info (#888)
+  - Update github actions to newer versions (669fe1b)
+
 - 1.4.2
   - create_dispatcher is determined by URL ws/wss, NOT by presence of sslopt 
args, to maintain consistency (#875)
   - Remove redundant key generation line (#864)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/websocket-client-1.4.2/PKG-INFO 
new/websocket-client-1.5.1/PKG-INFO
--- old/websocket-client-1.4.2/PKG-INFO 2022-11-04 05:02:49.798043000 +0100
+++ new/websocket-client-1.5.1/PKG-INFO 2023-02-04 18:58:33.128578000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: websocket-client
-Version: 1.4.2
+Version: 1.5.1
 Summary: WebSocket client for Python with low level API options
 Home-page: https://github.com/websocket-client/websocket-client.git
 Download-URL: https://github.com/websocket-client/websocket-client/releases
@@ -113,9 +113,10 @@
 
 `run_forever` provides a variety of event-based connection controls
 using callbacks like `on_message` and `on_error`.
-`run_forever` does not automatically reconnect if the server
+`run_forever` **does not automatically reconnect** if the server
 closes the WebSocket gracefully (returning
 [a standard websocket close 
code](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1)).
+[This is the 
logic](https://github.com/websocket-client/websocket-client/pull/838#issuecomment-1228454826)
 behind the decision.
 Customizing behavior when the server closes
 the WebSocket should be handled in the `on_close` callback.
 This example uses [rel](https://github.com/bubbleboy14/registeredeventlistener)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/websocket-client-1.4.2/README.md 
new/websocket-client-1.5.1/README.md
--- old/websocket-client-1.4.2/README.md        2022-10-26 03:38:57.000000000 
+0200
+++ new/websocket-client-1.5.1/README.md        2023-02-04 18:51:20.000000000 
+0100
@@ -80,9 +80,10 @@
 
 `run_forever` provides a variety of event-based connection controls
 using callbacks like `on_message` and `on_error`.
-`run_forever` does not automatically reconnect if the server
+`run_forever` **does not automatically reconnect** if the server
 closes the WebSocket gracefully (returning
 [a standard websocket close 
code](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1)).
+[This is the 
logic](https://github.com/websocket-client/websocket-client/pull/838#issuecomment-1228454826)
 behind the decision.
 Customizing behavior when the server closes
 the WebSocket should be handled in the `on_close` callback.
 This example uses [rel](https://github.com/bubbleboy14/registeredeventlistener)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/websocket-client-1.4.2/setup.py 
new/websocket-client-1.5.1/setup.py
--- old/websocket-client-1.4.2/setup.py 2022-11-04 04:57:54.000000000 +0100
+++ new/websocket-client-1.5.1/setup.py 2023-02-04 18:54:19.000000000 +0100
@@ -21,7 +21,7 @@
 limitations under the License.
 """
 
-VERSION = "1.4.2"
+VERSION = "1.5.1"
 
 install_requires = []
 tests_require = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/websocket-client-1.4.2/websocket/__init__.py 
new/websocket-client-1.5.1/websocket/__init__.py
--- old/websocket-client-1.4.2/websocket/__init__.py    2022-11-04 
04:58:05.000000000 +0100
+++ new/websocket-client-1.5.1/websocket/__init__.py    2023-02-04 
18:54:12.000000000 +0100
@@ -23,4 +23,4 @@
 from ._logging import *
 from ._socket import *
 
-__version__ = "1.4.2"
+__version__ = "1.5.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/websocket-client-1.4.2/websocket/_app.py 
new/websocket-client-1.5.1/websocket/_app.py
--- old/websocket-client-1.4.2/websocket/_app.py        2022-11-04 
04:51:47.000000000 +0100
+++ new/websocket-client-1.5.1/websocket/_app.py        2023-02-04 
18:51:20.000000000 +0100
@@ -4,11 +4,12 @@
 import threading
 import time
 import traceback
+
+from . import _logging
 from ._abnf import ABNF
 from ._url import parse_url
 from ._core import WebSocket, getdefaulttimeout
 from ._exceptions import *
-from . import _logging
 
 """
 _app.py
@@ -53,10 +54,9 @@
 
     def reconnect(self, seconds, reconnector):
         try:
-            while True:
-                _logging.info("reconnect() - retrying in %s seconds [%s frames 
in stack]" % (seconds, len(inspect.stack())))
-                time.sleep(seconds)
-                reconnector(reconnecting=True)
+            _logging.info("reconnect() - retrying in %s seconds [%s frames in 
stack]" % (seconds, len(inspect.stack())))
+            time.sleep(seconds)
+            reconnector(reconnecting=True)
         except KeyboardInterrupt as e:
             _logging.info("User exited %s" % (e,))
 
@@ -214,6 +214,11 @@
         self.sock = None
         self.last_ping_tm = 0
         self.last_pong_tm = 0
+        self.ping_thread = None
+        self.stop_ping = None
+        self.ping_interval = 0
+        self.ping_timeout = None
+        self.ping_payload = ""
         self.subprotocols = subprotocols
         self.prepared_socket = socket
         self.has_errored = False
@@ -244,15 +249,31 @@
             self.sock.close(**kwargs)
             self.sock = None
 
-    def _send_ping(self, interval, event, payload):
-        while not event.wait(interval):
-            self.last_ping_tm = time.time()
+    def _start_ping_thread(self):
+        self.last_ping_tm = self.last_pong_tm = 0
+        self.stop_ping = threading.Event()
+        self.ping_thread = threading.Thread(target=self._send_ping)
+        self.ping_thread.daemon = True
+        self.ping_thread.start()
+
+    def _stop_ping_thread(self):
+        if self.stop_ping:
+            self.stop_ping.set()
+        if self.ping_thread and self.ping_thread.is_alive():
+            self.ping_thread.join(3)
+        self.last_ping_tm = self.last_pong_tm = 0
+
+    def _send_ping(self):
+        if self.stop_ping.wait(self.ping_interval):
+            return
+        while not self.stop_ping.wait(self.ping_interval):
             if self.sock:
+                self.last_ping_tm = time.time()
                 try:
-                    self.sock.ping(payload)
+                    _logging.debug("Sending ping")
+                    self.sock.ping(self.ping_payload)
                 except Exception as ex:
-                    _logging.warning("send_ping routine terminated: 
{}".format(ex))
-                    break
+                    _logging.debug("Failed to send ping: %s", ex)
 
     def run_forever(self, sockopt=None, sslopt=None,
                     ping_interval=0, ping_timeout=None,
@@ -331,10 +352,11 @@
             sslopt = {}
         if self.sock:
             raise WebSocketException("socket is already opened")
-        thread = None
+
+        self.ping_interval = ping_interval
+        self.ping_timeout = ping_timeout
+        self.ping_payload = ping_payload
         self.keep_running = True
-        self.last_ping_tm = 0
-        self.last_pong_tm = 0
 
         def teardown(close_frame=None):
             """
@@ -347,9 +369,7 @@
                 with the statusCode and reason from the provided frame.
             """
 
-            if thread and thread.is_alive():
-                event.set()
-                thread.join()
+            self._stop_ping_thread()
             self.keep_running = False
             if self.sock:
                 self.sock.close()
@@ -361,11 +381,15 @@
             self._callback(self.on_close, close_status_code, close_reason)
 
         def setSock(reconnecting=False):
+            if reconnecting and self.sock:
+                self.sock.shutdown()
+
             self.sock = WebSocket(
                 self.get_mask_key, sockopt=sockopt, sslopt=sslopt,
                 fire_cont_frame=self.on_cont_message is not None,
                 skip_utf8_validation=skip_utf8_validation,
                 enable_multithread=True)
+
             self.sock.settimeout(getdefaulttimeout())
             try:
                 self.sock.connect(
@@ -377,13 +401,16 @@
                     host=host, origin=origin, suppress_origin=suppress_origin,
                     proxy_type=proxy_type, socket=self.prepared_socket)
 
+                _logging.info("Websocket connected")
+
+                if self.ping_interval:
+                    self._start_ping_thread()
+
                 self._callback(self.on_open)
 
-                _logging.warning("websocket connected")
                 dispatcher.read(self.sock.sock, read, check)
             except (WebSocketConnectionClosedException, 
ConnectionRefusedError, KeyboardInterrupt, SystemExit, Exception) as e:
-                _logging.error("%s - %s" % (e, reconnect and "reconnecting" or 
"goodbye"))
-                reconnecting or handleDisconnect(e)
+                handleDisconnect(e, reconnecting)
 
         def read():
             if not self.keep_running:
@@ -396,6 +423,7 @@
                     return handleDisconnect(e)
                 else:
                     raise e
+
             if op_code == ABNF.OPCODE_CLOSE:
                 return teardown(frame)
             elif op_code == ABNF.OPCODE_PING:
@@ -410,7 +438,7 @@
                                frame.data, frame.fin)
             else:
                 data = frame.data
-                if op_code == ABNF.OPCODE_TEXT:
+                if op_code == ABNF.OPCODE_TEXT and not skip_utf8_validation:
                     data = data.decode("utf-8")
                 self._callback(self.on_data, data, frame.opcode, True)
                 self._callback(self.on_message, data)
@@ -418,10 +446,10 @@
             return True
 
         def check():
-            if (ping_timeout):
-                has_timeout_expired = time.time() - self.last_ping_tm > 
ping_timeout
+            if (self.ping_timeout):
+                has_timeout_expired = time.time() - self.last_ping_tm > 
self.ping_timeout
                 has_pong_not_arrived_after_last_ping = self.last_pong_tm - 
self.last_ping_tm < 0
-                has_pong_arrived_too_late = self.last_pong_tm - 
self.last_ping_tm > ping_timeout
+                has_pong_arrived_too_late = self.last_pong_tm - 
self.last_ping_tm > self.ping_timeout
 
                 if (self.last_ping_tm and
                         has_timeout_expired and
@@ -429,29 +457,35 @@
                     raise WebSocketTimeoutException("ping/pong timed out")
             return True
 
-        def handleDisconnect(e):
+        def handleDisconnect(e, reconnecting=False):
             self.has_errored = True
-            self._callback(self.on_error, e)
-            if isinstance(e, SystemExit):
-                # propagate SystemExit further
+            self._stop_ping_thread()
+            if not reconnecting:
+                self._callback(self.on_error, e)
+
+            if isinstance(e, (KeyboardInterrupt, SystemExit)):
+                teardown()
+                # Propagate further
                 raise
-            if reconnect and not isinstance(e, KeyboardInterrupt):
-                _logging.info("websocket disconnected (retrying in %s seconds) 
[%s frames in stack]" % (reconnect, len(inspect.stack())))
-                dispatcher.reconnect(reconnect, setSock)
+
+            if reconnect:
+                _logging.info("%s - reconnect" % e)
+                if custom_dispatcher:
+                    _logging.debug("Calling custom dispatcher reconnect [%s 
frames in stack]" % len(inspect.stack()))
+                    dispatcher.reconnect(reconnect, setSock)
             else:
+                _logging.error("%s - goodbye" % e)
                 teardown()
 
         custom_dispatcher = bool(dispatcher)
         dispatcher = self.create_dispatcher(ping_timeout, dispatcher, 
parse_url(self.url)[3])
 
-        if ping_interval:
-            event = threading.Event()
-            thread = threading.Thread(
-                target=self._send_ping, args=(ping_interval, event, 
ping_payload))
-            thread.daemon = True
-            thread.start()
-
         setSock()
+        if not custom_dispatcher and reconnect:
+            while self.keep_running:
+                _logging.debug("Calling dispatcher reconnect [%s frames in 
stack]" % len(inspect.stack()))
+                dispatcher.reconnect(reconnect, setSock)
+
         return self.has_errored
 
     def create_dispatcher(self, ping_timeout, dispatcher=None, is_ssl=False):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/websocket-client-1.4.2/websocket/_socket.py 
new/websocket-client-1.5.1/websocket/_socket.py
--- old/websocket-client-1.4.2/websocket/_socket.py     2022-10-26 
03:38:57.000000000 +0200
+++ new/websocket-client-1.5.1/websocket/_socket.py     2023-02-04 
18:52:14.000000000 +0100
@@ -151,7 +151,7 @@
             error_code = extract_error_code(exc)
             if error_code is None:
                 raise
-            if error_code != errno.EAGAIN or error_code != errno.EWOULDBLOCK:
+            if error_code != errno.EAGAIN and error_code != errno.EWOULDBLOCK:
                 raise
 
         sel = selectors.DefaultSelector()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/websocket-client-1.4.2/websocket/tests/test_app.py 
new/websocket-client-1.5.1/websocket/tests/test_app.py
--- old/websocket-client-1.4.2/websocket/tests/test_app.py      2022-11-04 
04:51:47.000000000 +0100
+++ new/websocket-client-1.5.1/websocket/tests/test_app.py      2023-02-04 
18:51:20.000000000 +0100
@@ -186,13 +186,13 @@
         app = ws.WebSocketApp('wss://tsock.us1.twilio.com/v3/wsconnect')
         app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping 
payload")
 
-    @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are 
disabled")
-    def testOpcodeBinary(self):
-        """ Test WebSocketApp binary opcode
-        """
-        # The lack of wss:// in the URL below is on purpose
-        app = ws.WebSocketApp('wss://streaming.vn.teslamotors.com/streaming/')
-        app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping 
payload")
+    # This is commented out because the URL no longer responds in the expected 
way
+    # @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are 
disabled")
+    # def testOpcodeBinary(self):
+    #     """ Test WebSocketApp binary opcode
+    #     """
+    #     app = 
ws.WebSocketApp('wss://streaming.vn.teslamotors.com/streaming/')
+    #     app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping 
payload")
 
     @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are 
disabled")
     def testBadPingInterval(self):
@@ -228,6 +228,91 @@
 
         self.assertRaises(ws.WebSocketConnectionClosedException, app.send, 
data="test if connection is closed")
 
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket 
server are disabled")
+    def testCallbackFunctionException(self):
+        """ Test callback function exception handling """
+
+        exc = None
+        passed_app = None
+
+        def on_open(app):
+            raise RuntimeError("Callback failed")
+
+        def on_error(app, err):
+            nonlocal passed_app
+            passed_app = app
+            nonlocal exc
+            exc = err
+
+        def on_pong(app, msg):
+            app.close()
+
+        app = ws.WebSocketApp('ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT, 
on_open=on_open, on_error=on_error, on_pong=on_pong)
+        app.run_forever(ping_interval=2, ping_timeout=1)
+
+        self.assertEqual(passed_app, app)
+        self.assertIsInstance(exc, RuntimeError)
+        self.assertEqual(str(exc), "Callback failed")
+
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket 
server are disabled")
+    def testCallbackMethodException(self):
+        """ Test callback method exception handling """
+
+        class Callbacks:
+            def __init__(self):
+                self.exc = None
+                self.passed_app = None
+                self.app = ws.WebSocketApp(
+                    'ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT,
+                    on_open=self.on_open,
+                    on_error=self.on_error,
+                    on_pong=self.on_pong
+                )
+                self.app.run_forever(ping_interval=2, ping_timeout=1)
+
+            def on_open(self, app):
+                raise RuntimeError("Callback failed")
+
+            def on_error(self, app, err):
+                self.passed_app = app
+                self.exc = err
+
+            def on_pong(self, app, msg):
+                app.close()
+
+        callbacks = Callbacks()
+
+        self.assertEqual(callbacks.passed_app, callbacks.app)
+        self.assertIsInstance(callbacks.exc, RuntimeError)
+        self.assertEqual(str(callbacks.exc), "Callback failed")
+
+    @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket 
server are disabled")
+    def testReconnect(self):
+        """ Test reconnect """
+        pong_count = 0
+        exc = None
+
+        def on_error(app, err):
+            nonlocal exc
+            exc = err
+
+        def on_pong(app, msg):
+            nonlocal pong_count
+            pong_count += 1
+            if pong_count == 1:
+                # First pong, shutdown socket, enforce read error
+                app.sock.shutdown()
+            if pong_count >= 2:
+                # Got second pong after reconnect
+                app.close()
+
+        app = ws.WebSocketApp('ws://127.0.0.1:' + LOCAL_WS_SERVER_PORT, 
on_pong=on_pong, on_error=on_error)
+        app.run_forever(ping_interval=2, ping_timeout=1, reconnect=3)
+
+        self.assertEqual(pong_count, 2)
+        self.assertIsInstance(exc, ValueError)
+        self.assertEqual(str(exc), "Invalid file object: None")
+
 
 if __name__ == "__main__":
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/websocket-client-1.4.2/websocket_client.egg-info/PKG-INFO 
new/websocket-client-1.5.1/websocket_client.egg-info/PKG-INFO
--- old/websocket-client-1.4.2/websocket_client.egg-info/PKG-INFO       
2022-11-04 05:02:49.000000000 +0100
+++ new/websocket-client-1.5.1/websocket_client.egg-info/PKG-INFO       
2023-02-04 18:58:33.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: websocket-client
-Version: 1.4.2
+Version: 1.5.1
 Summary: WebSocket client for Python with low level API options
 Home-page: https://github.com/websocket-client/websocket-client.git
 Download-URL: https://github.com/websocket-client/websocket-client/releases
@@ -113,9 +113,10 @@
 
 `run_forever` provides a variety of event-based connection controls
 using callbacks like `on_message` and `on_error`.
-`run_forever` does not automatically reconnect if the server
+`run_forever` **does not automatically reconnect** if the server
 closes the WebSocket gracefully (returning
 [a standard websocket close 
code](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1)).
+[This is the 
logic](https://github.com/websocket-client/websocket-client/pull/838#issuecomment-1228454826)
 behind the decision.
 Customizing behavior when the server closes
 the WebSocket should be handled in the `on_close` callback.
 This example uses [rel](https://github.com/bubbleboy14/registeredeventlistener)

Reply via email to