public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/python-urllib3] rawhide: Fix test failures (OpenSSL 4 and pyOpenSSL 26.3)
@ 2026-08-09 16:50 
  0 siblings, 0 replies; only message in thread
From:  @ 2026-08-09 16:50 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/python-urllib3
            Branch : rawhide
            Commit : d75d492045c1f0435d8508e84f093063e4a88135
            Author : Miro Hrončok <miro@hroncok.cz>
            Date   : 2026-07-27T22:45:42+02:00
            Stats  : +220/-69 in 4 file(s)
            URL    : https://src.fedoraproject.org/rpms/python-urllib3/c/d75d492045c1f0435d8508e84f093063e4a88135?branch=rawhide

            Log:
            Fix test failures (OpenSSL 4 and pyOpenSSL 26.3)

- Fixes: rhbz#2504593

Used LLM to identify and rebase upstream patch #5103

Assisted-By: Claude Opus 4.6

---
diff --git a/5097.patch b/5097.patch
new file mode 100644
index 0000000..ac3af0f
--- /dev/null
+++ b/5097.patch
@@ -0,0 +1,133 @@
+From 8d2659ac68ec8d46dfd8c2fc0ab057b647ac7940 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
+Date: Wed, 1 Jul 2026 15:05:18 +0200
+Subject: [PATCH] Deal with ssl.PROTOCOL_TLSv1 removal from Python built with
+ OpenSSL 4+
+
+https://github.com/python/cpython/commit/3364e7e62fa24d0e19133fb0f90b1c24ef1110c5
+removed ssl.PROTOCOL_TLSv1 when python is built with OpenSSL 4+.
+
+We are hitting this error in Fedora (where we already updated OpenSSL):
+
+    ImportError while loading conftest '/builddir/build/BUILD/python-urllib3-2.7.0-build/urllib3-2.7.0/test/conftest.py'.
+    test/__init__.py:42: in <module>
+        import urllib3.contrib.pyopenssl as pyopenssl
+    ../BUILDROOT/usr/lib/python3.15/site-packages/urllib3/contrib/pyopenssl.py:72: in <module>
+        ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
+        ^^^^^^^^^^^^^^^^^^
+    E   AttributeError: module 'ssl' has no attribute 'PROTOCOL_TLSv1'. Did you mean '.PROTOCOL_TLS' instead of '.PROTOCOL_TLSv1'?
+
+Used LLM to figure out what to use as a replacement protocol in tests.
+
+Assisted-By: Claude Opus 4.6
+---
+ changelog/5097.bugfix.rst        |  2 ++
+ src/urllib3/contrib/pyopenssl.py |  4 +++-
+ test/test_ssl.py                 | 14 ++++++++------
+ test/test_util.py                | 24 +++++++++++++++---------
+ 4 files changed, 28 insertions(+), 16 deletions(-)
+ create mode 100644 changelog/5097.bugfix.rst
+
+diff --git a/changelog/5097.bugfix.rst b/changelog/5097.bugfix.rst
+new file mode 100644
+index 0000000000..73ed0ddaa1
+--- /dev/null
++++ b/changelog/5097.bugfix.rst
+@@ -0,0 +1,2 @@
++Fixed an ``AttributeError`` on Python built with OpenSSL 4+, where
++``ssl.PROTOCOL_TLSv1`` no longer exists.
+diff --git a/src/urllib3/contrib/pyopenssl.py b/src/urllib3/contrib/pyopenssl.py
+index 42781590ee..76a225b7bf 100644
+--- a/src/urllib3/contrib/pyopenssl.py
++++ b/src/urllib3/contrib/pyopenssl.py
+@@ -68,9 +68,11 @@ class UnsupportedExtension(Exception):  # type: ignore[no-redef]
+ _openssl_versions: dict[int, int] = {
+     util.ssl_.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD,  # type: ignore[attr-defined]
+     util.ssl_.PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD,  # type: ignore[attr-defined]
+-    ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
+ }
+ 
++if hasattr(ssl, "PROTOCOL_TLSv1") and hasattr(OpenSSL.SSL, "TLSv1_METHOD"):
++    _openssl_versions[ssl.PROTOCOL_TLSv1] = OpenSSL.SSL.TLSv1_METHOD
++
+ if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"):
+     _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD
+ 
+diff --git a/test/test_ssl.py b/test/test_ssl.py
+index 6b1ab8589b..1e4063bf68 100644
+--- a/test/test_ssl.py
++++ b/test/test_ssl.py
+@@ -176,19 +176,21 @@ def test_create_urllib3_context_default_ciphers(
+ 
+         context.set_ciphers.assert_not_called()
+ 
++    # PROTOCOL_TLS_SERVER is used as a stand-in for any non-default ssl_version.
++    # PROTOCOL_TLSv1/TLSv1_2 are unavailable when Python is built with OpenSSL 4+.
+     @pytest.mark.parametrize(
+         "kwargs",
+         [
+             {
+-                "ssl_version": ssl.PROTOCOL_TLSv1,
++                "ssl_version": ssl.PROTOCOL_TLS_SERVER,
+                 "ssl_minimum_version": ssl.TLSVersion.MINIMUM_SUPPORTED,
+             },
+             {
+-                "ssl_version": ssl.PROTOCOL_TLSv1,
++                "ssl_version": ssl.PROTOCOL_TLS_SERVER,
+                 "ssl_maximum_version": ssl.TLSVersion.TLSv1,
+             },
+             {
+-                "ssl_version": ssl.PROTOCOL_TLSv1,
++                "ssl_version": ssl.PROTOCOL_TLS_SERVER,
+                 "ssl_minimum_version": ssl.TLSVersion.MINIMUM_SUPPORTED,
+                 "ssl_maximum_version": ssl.TLSVersion.MAXIMUM_SUPPORTED,
+             },
+@@ -229,10 +231,10 @@ def test_create_urllib3_context_ssl_version_and_ssl_min_max_version_no_warning(
+     @pytest.mark.parametrize(
+         "kwargs",
+         [
+-            {"ssl_version": ssl.PROTOCOL_TLSv1, "ssl_minimum_version": None},
+-            {"ssl_version": ssl.PROTOCOL_TLSv1, "ssl_maximum_version": None},
++            {"ssl_version": ssl.PROTOCOL_TLS_SERVER, "ssl_minimum_version": None},
++            {"ssl_version": ssl.PROTOCOL_TLS_SERVER, "ssl_maximum_version": None},
+             {
+-                "ssl_version": ssl.PROTOCOL_TLSv1,
++                "ssl_version": ssl.PROTOCOL_TLS_SERVER,
+                 "ssl_minimum_version": None,
+                 "ssl_maximum_version": None,
+             },
+diff --git a/test/test_util.py b/test/test_util.py
+index 8612ab5eaf..18c65f0312 100644
+--- a/test/test_util.py
++++ b/test/test_util.py
+@@ -1216,15 +1216,21 @@ def test_resolve_cert_reqs(
+     ) -> None:
+         assert resolve_cert_reqs(candidate) == requirements
+ 
+-    @pytest.mark.parametrize(
+-        "candidate, version",
+-        [
+-            (ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1),
+-            ("PROTOCOL_TLSv1", ssl.PROTOCOL_TLSv1),
+-            ("TLSv1", ssl.PROTOCOL_TLSv1),
+-            (ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23),
+-        ],
+-    )
++    candidate_version = [
++        (ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23),
++        ("PROTOCOL_SSLv23", ssl.PROTOCOL_SSLv23),
++        ("SSLv23", ssl.PROTOCOL_SSLv23),
++    ]
++    if hasattr(ssl, "PROTOCOL_TLSv1"):
++        candidate_version.extend(
++            [
++                (ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1),
++                ("PROTOCOL_TLSv1", ssl.PROTOCOL_TLSv1),
++                ("TLSv1", ssl.PROTOCOL_TLSv1),
++            ]
++        )
++
++    @pytest.mark.parametrize("candidate, version", candidate_version)
+     def test_resolve_ssl_version(self, candidate: int | str, version: int) -> None:
+         assert resolve_ssl_version(candidate) == version
+ 

diff --git a/5103.patch b/5103.patch
new file mode 100644
index 0000000..c1b28d5
--- /dev/null
+++ b/5103.patch
@@ -0,0 +1,83 @@
+From 13fa1e033b37405b228599ee590af485dc3e38aa Mon Sep 17 00:00:00 2001
+From: Illia Volochii <illia.volochii@gmail.com>
+Date: Sat, 18 Jul 2026 13:25:41 +0200
+Subject: [PATCH] Replace deprecated pyOpenSSL `X509.get_subject` and
+ `Context.set_passwd_cb` methods (#5103)
+
+Rebased on top of urllib3-2.7.0.
+---
+ src/urllib3/contrib/pyopenssl.py | 35 ++++++++++++++++++++++++++-------
+ 1 file changed, 28 insertions(+), 7 deletions(-)
+
+diff --git a/src/urllib3/contrib/pyopenssl.py b/src/urllib3/contrib/pyopenssl.py
+index 76a225b7bf..a1b2c3d4e5 100644
+--- a/src/urllib3/contrib/pyopenssl.py
++++ b/src/urllib3/contrib/pyopenssl.py
+@@ -42,6 +42,8 @@
+
+ import OpenSSL.SSL  # type: ignore[import-not-found]
+ from cryptography import x509
++from cryptography.hazmat.primitives.serialization import load_pem_private_key
++from cryptography.x509.oid import NameOID
+
+ try:
+     from cryptography.x509 import UnsupportedExtension  # type: ignore[attr-defined]
+@@ -272,6 +274,15 @@
+     return names
+
+
++def _get_common_name(peer_cert: X509) -> str | None:
++    """
++    Given a pyOpenSSL certificate, return the subject's common name.
++    """
++    cert = peer_cert.to_cryptography()
++    names = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
++    return typing.cast(str, names[0].value) if names else None
++
++
+ class WrappedSocket:
+     """API-compatibility wrapper for Python OpenSSL's Connection-class."""
+
+@@ -396,7 +407,7 @@
+             return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509)  # type: ignore[no-any-return]
+
+         return {
+-            "subject": ((("commonName", x509.get_subject().CN),),),  # type: ignore[dict-item]
++            "subject": ((("commonName", _get_common_name(x509)),),),  # type: ignore[dict-item]
+             "subjectAltName": get_subj_alt_name(x509),
+         }
+
+@@ -482,16 +493,28 @@
+         self,
+         certfile: str,
+         keyfile: str | None = None,
+-        password: str | None = None,
++        password: str | bytes | None = None,
+     ) -> None:
+         try:
+             self._ctx.use_certificate_chain_file(certfile)
+             if password is not None:
+                 if not isinstance(password, bytes):
+-                    password = password.encode("utf-8")  # type: ignore[assignment]
+-                self._ctx.set_passwd_cb(lambda *_: password)
+-            self._ctx.use_privatekey_file(keyfile or certfile)
+-        except OpenSSL.SSL.Error as e:
++                    password = password.encode("utf-8")
++                # pyOpenSSL added cryptography-key support in 24.3.0.
++                # Keep using the older password-callback path until 2026's
++                # versions because set_passwd_cb() became deprecated in 26.3.0.
++                if int(OpenSSL.__version__.split(".")[0]) >= 26:
++                    with open(keyfile or certfile, "rb") as key_file:
++                        private_key = load_pem_private_key(key_file.read(), password)
++                    # cryptography's loader returns a wider private-key union
++                    # than pyOpenSSL accepts, so we add `type: ignore` here.
++                    self._ctx.use_privatekey(private_key)  # type: ignore[arg-type]
++                else:
++                    self._ctx.set_passwd_cb(lambda *_: password)
++                    self._ctx.use_privatekey_file(keyfile or certfile)
++            else:
++                self._ctx.use_privatekey_file(keyfile or certfile)
++        except (OpenSSL.SSL.Error, TypeError, ValueError) as e:
+             raise ssl.SSLError(f"Unable to load certificate chain: {e!r}") from e
+
+     def set_alpn_protocols(self, protocols: list[bytes | str]) -> None:

diff --git a/python-urllib3-py315-ssl.patch b/python-urllib3-py315-ssl.patch
deleted file mode 100644
index ec0d1fb..0000000
--- a/python-urllib3-py315-ssl.patch
+++ /dev/null
@@ -1,67 +0,0 @@
---- a/src/urllib3/contrib/pyopenssl.py
-+++ b/src/urllib3/contrib/pyopenssl.py
-@@ -69,9 +69,11 @@
- _openssl_versions: dict[int, int] = {
-     util.ssl_.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD,  # type: ignore[attr-defined]
-     util.ssl_.PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD,  # type: ignore[attr-defined]
--    ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
- }
- 
-+if hasattr(ssl, "PROTOCOL_TLSv1") and hasattr(OpenSSL.SSL, "TLSv1_METHOD"):
-+    _openssl_versions[ssl.PROTOCOL_TLSv1] = OpenSSL.SSL.TLSv1_METHOD
-+
- if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"):
-     _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD
- 
---- a/test/test_ssl.py
-+++ b/test/test_ssl.py
-@@ -173,15 +173,15 @@
-         "kwargs",
-         [
-             {
--                "ssl_version": ssl.PROTOCOL_TLSv1,
-+                "ssl_version": getattr(ssl, "PROTOCOL_TLSv1", getattr(ssl, "PROTOCOL_TLSv1_2", 1)),
-                 "ssl_minimum_version": ssl.TLSVersion.MINIMUM_SUPPORTED,
-             },
-             {
--                "ssl_version": ssl.PROTOCOL_TLSv1,
-+                "ssl_version": getattr(ssl, "PROTOCOL_TLSv1", getattr(ssl, "PROTOCOL_TLSv1_2", 1)),
-                 "ssl_maximum_version": ssl.TLSVersion.TLSv1,
-             },
-             {
--                "ssl_version": ssl.PROTOCOL_TLSv1,
-+                "ssl_version": getattr(ssl, "PROTOCOL_TLSv1", getattr(ssl, "PROTOCOL_TLSv1_2", 1)),
-                 "ssl_minimum_version": ssl.TLSVersion.MINIMUM_SUPPORTED,
-                 "ssl_maximum_version": ssl.TLSVersion.MAXIMUM_SUPPORTED,
-             },
-@@ -222,10 +222,10 @@
-     @pytest.mark.parametrize(
-         "kwargs",
-         [
--            {"ssl_version": ssl.PROTOCOL_TLSv1, "ssl_minimum_version": None},
--            {"ssl_version": ssl.PROTOCOL_TLSv1, "ssl_maximum_version": None},
-+            {"ssl_version": getattr(ssl, "PROTOCOL_TLSv1", getattr(ssl, "PROTOCOL_TLSv1_2", 1)), "ssl_minimum_version": None},
-+            {"ssl_version": getattr(ssl, "PROTOCOL_TLSv1", getattr(ssl, "PROTOCOL_TLSv1_2", 1)), "ssl_maximum_version": None},
-             {
--                "ssl_version": ssl.PROTOCOL_TLSv1,
-+                "ssl_version": getattr(ssl, "PROTOCOL_TLSv1", getattr(ssl, "PROTOCOL_TLSv1_2", 1)),
-                 "ssl_minimum_version": None,
-                 "ssl_maximum_version": None,
-             },
---- a/test/test_util.py
-+++ b/test/test_util.py
-@@ -982,9 +982,11 @@
-     @pytest.mark.parametrize(
-         "candidate, version",
-         [
--            (ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1),
--            ("PROTOCOL_TLSv1", ssl.PROTOCOL_TLSv1),
--            ("TLSv1", ssl.PROTOCOL_TLSv1),
-+            *([
-+                (getattr(ssl, "PROTOCOL_TLSv1", None), getattr(ssl, "PROTOCOL_TLSv1", None)),
-+                ("PROTOCOL_TLSv1", getattr(ssl, "PROTOCOL_TLSv1", None)),
-+                ("TLSv1", getattr(ssl, "PROTOCOL_TLSv1", None)),
-+            ] if hasattr(ssl, "PROTOCOL_TLSv1") else []),
-             (ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23),
-         ],
-     )

diff --git a/python-urllib3.spec b/python-urllib3.spec
index b4683e8..49ec528 100644
--- a/python-urllib3.spec
+++ b/python-urllib3.spec
@@ -32,8 +32,10 @@ Source0:        %{url}/archive/%{version}/urllib3-%{version}.tar.gz
 Source1:        %{hypercorn_url}/archive/%{hypercorn_commit}/hypercorn-%{hypercorn_commit}.tar.gz
 
 # Deal with ssl.PROTOCOL_TLSv1 removal from Python built with OpenSSL 4+
-# https://github.com/urllib3/urllib3/pull/5097
-Patch:          python-urllib3-py315-ssl.patch
+Patch:          https://github.com/urllib3/urllib3/pull/5097.patch
+# Replace deprecated pyOpenSSL X509.get_subject and Context.set_passwd_cb methods
+# https://github.com/urllib3/urllib3/pull/5103 rebased
+Patch:          5103.patch
 
 BuildArch:      noarch
 

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-08-09 16:50 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-09 16:50 [rpms/python-urllib3] rawhide: Fix test failures (OpenSSL 4 and pyOpenSSL 26.3) 

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox