yarl: pickling of URL in python 3.15

Maximiliano Curia <[email protected]>
Newsgroups gmane.linux.debian.devel.python
Message-ID <[email protected]>
Package: src:yarl
Version: 1.23.0-2
User: [email protected]
Usertags: python3.15

Hi!

While rebuilding packages against Python 3.15 we found that yarl failed
to build from source due to an incompatibility on the way pickle handles
__getstate__. This is already fixed upstream in the 1.24.0 release with
the commit: faaa1fd Fix pickling of URL on Python 3.15 (#1687)

I'm attaching a debdiff that adds this patch to the current version, as
that's what I've pushed to the Python 3.15 sandbox
(https://debusine.debian.net/debian/r-python-python3.15/) in order to be
able to keep building the packages that depend on yarl.

Please consider either applying the upstream fix or pushing the new
upstream release.

Happy hacking,
-- 
"Nothing ever goes away." -- Commoner's Law of Ecology
Saludos /\/\ /\ >< `/
yarl_1.23.0-2.1.debdiff (text/plain, 4.8 KB)
diff -Nru yarl-1.23.0/debian/changelog yarl-1.23.0/debian/changelog
--- yarl-1.23.0/debian/changelog	2026-07-21 17:47:36.000000000 +0200
+++ yarl-1.23.0/debian/changelog	2026-08-15 23:19:46.000000000 +0200
@@ -1,3 +1,9 @@
+yarl (1.23.0-2.1) UNRELEASED; urgency=medium
+
+  * Non-maintainer upload.
+
+ -- Maximiliano Curia <[email protected]>  Sat, 15 Aug 2026 23:19:46 +0200
+
 yarl (1.23.0-2) unstable; urgency=medium
 
   * Team upload.
diff -Nru yarl-1.23.0/debian/patches/fix-pickling-of-url-on-python-315.patch yarl-1.23.0/debian/patches/fix-pickling-of-url-on-python-315.patch
--- yarl-1.23.0/debian/patches/fix-pickling-of-url-on-python-315.patch	1970-01-01 01:00:00.000000000 +0100
+++ yarl-1.23.0/debian/patches/fix-pickling-of-url-on-python-315.patch	2026-08-15 23:19:46.000000000 +0200
@@ -0,0 +1,88 @@
+commit faaa1fdd975220810dffb91adad87d589931d5b8
+Author: aio-libs bot <[email protected]>
+Date:   Sun May 17 06:57:50 2026 -0700
+
+    Fix pickling of URL on Python 3.15 (#1687)
+
+diff --git a/CHANGES/1632.bugfix.rst b/CHANGES/1632.bugfix.rst
+new file mode 100644
+index 0000000..0e857c5
+--- /dev/null
++++ b/CHANGES/1632.bugfix.rst
+@@ -0,0 +1,7 @@
++Fixed pickling of :class:`~yarl.URL` on Python 3.15, where ``SplitResult``
++gained a ``__getstate__`` that requires attributes set by ``__init__``.
++``__getstate__`` now returns the raw 5-tuple instead of a ``SplitResult``
++built via ``tuple.__new__``, so pickling no longer touches ``SplitResult``
++serialization at all. Pickles produced by older yarl releases (which embed
++a ``SplitResult``) continue to load unchanged
++-- by :user:`aiolibsbot`.
+diff --git a/tests/test_pickle.py b/tests/test_pickle.py
+index c3630f6..6939664 100644
+--- a/tests/test_pickle.py
++++ b/tests/test_pickle.py
+@@ -1,4 +1,5 @@
+ import pickle
++from urllib.parse import SplitResult
+ 
+ from yarl import URL
+ 
+@@ -58,3 +59,36 @@ def test_pickle_does_not_pollute_cache() -> None:
+     # for empty args.
+     assert URL().scheme == ""
+     assert URL("").scheme == ""
++
++
++def test_pickle_legacy_splitresult_state() -> None:
++    """Pickles produced by older yarl releases embedded a ``SplitResult``.
++
++    Loading such bytes must still rebuild the URL so users upgrading from
++    pre-fix versions do not lose access to their stored data.
++    """
++    val = ("http", "example.com", "/p", "q=1", "frag")
++    legacy_state = (tuple.__new__(SplitResult, val),)
++    u = URL.__new__(URL)
++    u.__setstate__(legacy_state)
++    assert u._scheme == "http"
++    assert u._netloc == "example.com"
++    assert u._path == "/p"
++    assert u._query == "q=1"
++    assert u._fragment == "frag"
++
++
++def test_pickle_getstate_returns_plain_tuple() -> None:
++    """Regression test for gh-1632.
++
++    Python 3.15 added a ``SplitResult.__getstate__`` that touches instance
++    attributes set by ``__init__``. yarl previously embedded a ``SplitResult``
++    built via ``tuple.__new__``, bypassing init, which made pickling crash.
++    ``__getstate__`` must return a plain ``tuple`` so pickling never invokes
++    ``SplitResult.__getstate__``.
++    """
++    u = URL("http://example.com/path?q=1#frag")
++    state = u.__getstate__()
++    assert len(state) == 1
++    assert type(state[0]) is tuple
++    assert state[0] == ("http", "example.com", "/path", "q=1", "frag")
+diff --git a/yarl/_url.py b/yarl/_url.py
+index aec8d2b..5a661a4 100644
+--- a/yarl/_url.py
++++ b/yarl/_url.py
+@@ -566,8 +566,15 @@ class URL:
+     def __bool__(self) -> bool:
+         return bool(self._netloc or self._path or self._query or self._fragment)
+ 
+-    def __getstate__(self) -> tuple[SplitResult]:
+-        return (tuple.__new__(SplitResult, self._val),)
++    def __getstate__(self) -> tuple[SplitURLType]:
++        # Return a plain tuple rather than a ``SplitResult``. Constructing a
++        # ``SplitResult`` via ``tuple.__new__`` skips its ``__init__`` and on
++        # Python 3.15+ leaves ``_keep_empty`` unset, which breaks pickling: the
++        # new ``SplitResult.__getstate__`` indexes a state that ends up as
++        # ``None`` (gh-1632). ``__setstate__`` already unpacks both shapes, so
++        # pickles produced by older yarl releases (which embed a real
++        # ``SplitResult``) still load correctly.
++        return (self._val,)
+ 
+     def __setstate__(
+         self, state: tuple[SplitURLType] | tuple[None, _InternalURLCache]
diff -Nru yarl-1.23.0/debian/patches/series yarl-1.23.0/debian/patches/series
--- yarl-1.23.0/debian/patches/series	2026-07-21 17:34:50.000000000 +0200
+++ yarl-1.23.0/debian/patches/series	2026-08-15 23:19:46.000000000 +0200
@@ -4,3 +4,4 @@
 0004-disable-privacy-breach-links-in-documentation.patch
 0005-drop-towncrier-changelog-generation-for-now.patch
 0006-use-os.path.expandvars-instead-of-external-one.patch
+fix-pickling-of-url-on-python-315.patch
signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAABCgAdFiEE+JIdOnQEyG4RNSIVxxl2mbKbIyoFAmqBjgAACgkQxxl2mbKb
IyooLQ//QucrmmZ0nwT4Uac5YWIzob83AaiQkqaMb1pDsA0MHBc+q1LINJ5I4Ieg
PSnQ0MeGpeObyHuYOcWWZWMsi7L6a+ZLXJGOFVJXj/1jyLI2PKh+JFGeO2vYMkCn
3cZEax5l8Or1evAttUukC1nCGgHJaAPxZPdDEAXce3XSo2BxD1/+/9hnvIzJ3Y5u
CNZoZeG+QuBo/0EgLvRZ2rpHFjBiLY0i1amt/1fNOs9RWw2hwHpZX79uXP0h6nmA
FrSZkWgCaO5Se6u4BdTpOOerGoUBTEipv5d00JKaE0yo2nilBOR15vqo+AXGi65t
Qif0t8biH5j5hjA0RlLWOn+c3NEGnA8jBzVPm4WMCyUkghc4z7TRWoKX6ef5LEx3
os3tugKNlrpLSFtD/+/21bfrt7bHJYTm4hEAgxt8L5ENGUlJLsTw6hP0w/EHLnvX
owM5fpTsr6L1WPACqvtgkPR1e1jRqj/OnndMxzaZTxLJ6UnDPPBDd7BI0rdUt7C+
Tyn80nF2RZCOmU/XFiTHFKrbqwvFBmpEMsg+0OfV5kR5XzoKEbWffCaCsqcamLs5
xK1UDqkUR3TSNpOpMPgtFNJm7cv2xD8dSgjvzIYixVMOewFweKtyvWBmAKy+dLNY
XnFOhciOiIuLrvl5AfK1+AzmoYbs7WxxV1n53JkqJWPpSnwBYQs=
=4/w/
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.