Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-sushy for openSUSE:Factory 
checked in at 2026-04-12 17:52:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-sushy (Old)
 and      /work/SRC/openSUSE:Factory/.python-sushy.new.21863 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-sushy"

Sun Apr 12 17:52:24 2026 rev:22 rq:1345703 version:5.10.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-sushy/python-sushy.changes        
2026-03-04 21:10:14.060887173 +0100
+++ /work/SRC/openSUSE:Factory/.python-sushy.new.21863/python-sushy.changes     
2026-04-12 17:52:26.291474012 +0200
@@ -1,0 +2,5 @@
+Thu Apr  9 12:37:24 UTC 2026 - Nicolas Belouin <[email protected]>
+
+- Add Prevent-double-wrapping-timeout-tuple-in-Connector-_op.patch
+
+-------------------------------------------------------------------

New:
----
  Prevent-double-wrapping-timeout-tuple-in-Connector-_op.patch

----------(New B)----------
  New:
- Add Prevent-double-wrapping-timeout-tuple-in-Connector-_op.patch
----------(New E)----------

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

Other differences:
------------------
++++++ python-sushy.spec ++++++
--- /var/tmp/diff_new_pack.aJvOhU/_old  2026-04-12 17:52:27.007503168 +0200
+++ /var/tmp/diff_new_pack.aJvOhU/_new  2026-04-12 17:52:27.011503331 +0200
@@ -24,6 +24,8 @@
 Group:          Development/Languages/Python
 URL:            https://docs.openstack.org/sushy
 Source0:        
https://files.pythonhosted.org/packages/source/s/sushy/sushy-%{version}.tar.gz
+# [PATCH] Prevent double-wrapping timeout tuple in Connector _op
+Patch0:         Prevent-double-wrapping-timeout-tuple-in-Connector-_op.patch
 BuildRequires:  %{python_module oslotest}
 BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module pytest}

++++++ Prevent-double-wrapping-timeout-tuple-in-Connector-_op.patch ++++++
>From a18eaf3aed8366027d3d2decfcdf6b2318c1c995 Mon Sep 17 00:00:00 2001
From: Lennart Jern <[email protected]>
Date: Thu, 26 Mar 2026 10:04:57 +0000
Subject: [PATCH] Prevent double-wrapping timeout tuple in Connector _op

Add test to ensure timeout is not nested on recursive retry when
connect_timeout is set.

Closes-Bug: 2146416
Change-Id: I9e7ee050b6092dbc5529b519c24c18e98008f6e4
Signed-off-by: Lennart Jern <[email protected]>
(cherry picked from commit b87365bdc29c91a8c88c18c8577820f85d9c712f)
---
 ...-timeout-double-wrap-72ed19391dc0b470.yaml | 12 ++++++++
 sushy/connector.py                            |  3 +-
 sushy/tests/unit/test_connector.py            | 30 +++++++++++++++++++
 3 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 
releasenotes/notes/fix-connect-timeout-double-wrap-72ed19391dc0b470.yaml

diff --git 
a/releasenotes/notes/fix-connect-timeout-double-wrap-72ed19391dc0b470.yaml 
b/releasenotes/notes/fix-connect-timeout-double-wrap-72ed19391dc0b470.yaml
new file mode 100644
index 00000000..35d2b5bf
--- /dev/null
+++ b/releasenotes/notes/fix-connect-timeout-double-wrap-72ed19391dc0b470.yaml
@@ -0,0 +1,12 @@
+---
+fixes:
+  - |
+    Fixes a bug where the ``connect_timeout`` parameter caused an
+    ``Invalid timeout`` error on any HTTP retry inside ``Connector._op()``.
+    When ``connect_timeout`` is set, the timeout is wrapped into a
+    ``(connect, read)`` tuple, but on recursive retries (server-side errors,
+    re-authentication, or redirect following) the already-wrapped tuple was
+    wrapped again, producing a nested tuple like ``(30, (30, 60))`` that the
+    ``requests`` library rejects. The timeout tuple is now passed through
+    unchanged on retries.
+    See `LP#2146416 <https://bugs.launchpad.net/sushy/+bug/2146416>`_.
diff --git a/sushy/connector.py b/sushy/connector.py
index bef7ad02..9a8fab39 100644
--- a/sushy/connector.py
+++ b/sushy/connector.py
@@ -147,7 +147,8 @@ class Connector:
         timeout = timeout or self._default_request_timeout
         # If connect_timeout is configured, use a tuple (connect, read) for
         # the requests timeout to allow faster failure on unreachable BMCs.
-        if self._connect_timeout is not None:
+        if (self._connect_timeout is not None
+                and not isinstance(timeout, tuple)):
             timeout = (self._connect_timeout, timeout)
 
         url = path if urlparse.urlparse(path).netloc else urlparse.urljoin(
diff --git a/sushy/tests/unit/test_connector.py 
b/sushy/tests/unit/test_connector.py
index be077da0..60596784 100644
--- a/sushy/tests/unit/test_connector.py
+++ b/sushy/tests/unit/test_connector.py
@@ -203,6 +203,36 @@ class ConnectorOpTestCase(base.TestCase):
             'GET', 'http://foo.bar:1234/fake/path',
             headers=self.headers, json=None, verify=True, timeout=(10, 60))
 
+    def test_connect_timeout_not_double_wrapped_on_retry(self):
+        """Test that timeout tuple is not double-wrapped on recursive retry.
+
+        When connect_timeout is set, _op() wraps timeout into a
+        (connect, read) tuple. On retry _op() calls itself recursively,
+        passing the already-wrapped tuple. Without the isinstance guard
+        this results in (connect, (connect, read)) which requests rejects.
+        Regression test for https://bugs.launchpad.net/sushy/+bug/2146416
+        """
+        self.conn._connect_timeout = 10
+        self.conn._default_request_timeout = 60
+        self.request.side_effect = [
+            mock.Mock(status_code=http_client.NOT_ACCEPTABLE),
+            mock.Mock(status_code=http_client.OK),
+        ]
+        self.conn._op('GET', 'http://foo.bar')
+
+        self.assertEqual(2, self.request.call_count)
+        headers_no_accept = self.headers.copy()
+        headers_no_accept.pop('Accept-Encoding')
+        # Both the original and the retried request must use the same
+        # (connect, read) timeout tuple - never a nested tuple.
+        self.request.assert_has_calls([
+            mock.call('GET', 'http://foo.bar', headers=self.headers,
+                      json=None, verify=True, timeout=(10, 60)),
+            mock.call('GET', 'http://foo.bar',
+                      headers=headers_no_accept,
+                      json=None, verify=True, timeout=(10, 60)),
+        ])
+
     def test_response_callback(self):
         mock_response_callback = mock.MagicMock()
         self.conn._response_callback = mock_response_callback

Reply via email to