https://github.com/python/cpython/commit/a4a5a06767e03c09d169480f82da944fae663db1
commit: a4a5a06767e03c09d169480f82da944fae663db1
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-12T09:29:19Z
summary:

gh-61366: Write session cookies in the curl and Wget format (GH-155602)

Write 0 in the expiration time field for session cookies.
curl and Wget ignore the line if this field is empty.

files:
A Misc/NEWS.d/next/Library/2026-08-12-09-30-00.gh-issue-61366.Wr7cKm.rst
M Lib/http/cookiejar.py
M Lib/test/test_http_cookiejar.py

diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py
index 81526cd544acfb..144db91a0f0317 100644
--- a/Lib/http/cookiejar.py
+++ b/Lib/http/cookiejar.py
@@ -2109,7 +2109,9 @@ def save(self, filename=None, ignore_discard=False, 
ignore_expires=False):
                 if cookie.expires is not None:
                     expires = str(cookie.expires)
                 else:
-                    expires = ""
+                    # curl and Wget use 0 for session cookies, and ignore
+                    # the line if this field is empty.
+                    expires = "0"
                 if cookie.value is None:
                     # cookies.txt regards 'Set-Cookie: foo' as a cookie
                     # with no name, whereas http.cookiejar regards it as a
diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py
index 7c31cff56000b8..218181f1185365 100644
--- a/Lib/test/test_http_cookiejar.py
+++ b/Lib/test/test_http_cookiejar.py
@@ -2048,6 +2048,35 @@ def test_session_cookies(self):
             # we didn't have session cookies in the first place
         self.assertNotEqual(counter["session_before"], 0)
 
+    def test_save_session_cookies(self):
+        # Session cookies are saved with 0 in the expiration time field,
+        # as curl and Wget do (gh-61366).
+        filename = os_helper.TESTFN
+        self.addCleanup(os_helper.unlink, filename)
+        expires = int(time.time() + 3600)
+        c = MozillaCookieJar()
+        c.set_cookie(Cookie(0, "perm", "bar", None, False,
+                            "www.foo.com", True, False, "/", False, False,
+                            expires, False, None, None, {}))
+        c.set_cookie(Cookie(0, "session", "bar", None, False,
+                            "www.foo.com", True, False, "/", False, False,
+                            None, True, None, None, {}))
+        c.save(filename, ignore_discard=True)
+
+        saved = {}
+        with open(filename) as f:
+            for line in f:
+                if line.strip() and not line.startswith("#"):
+                    fields = line.split("\t")
+                    saved[fields[5]] = fields[4]
+        self.assertEqual(saved, {"perm": str(expires), "session": "0"})
+
+        # The saved file can be read back.
+        c = MozillaCookieJar()
+        c.revert(filename, ignore_discard=True)
+        self.assertEqual(sorted(cookie.name for cookie in c),
+                         ["perm", "session"])
+
     def test_load_session_cookies(self):
         # curl and Wget write 0 in the expires field for session cookies,
         # while we write an empty field.  Both should be read (gh-61366).
diff --git 
a/Misc/NEWS.d/next/Library/2026-08-12-09-30-00.gh-issue-61366.Wr7cKm.rst 
b/Misc/NEWS.d/next/Library/2026-08-12-09-30-00.gh-issue-61366.Wr7cKm.rst
new file mode 100644
index 00000000000000..662d55f05abda6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-12-09-30-00.gh-issue-61366.Wr7cKm.rst
@@ -0,0 +1,3 @@
+:meth:`!http.cookiejar.MozillaCookieJar.save` now writes ``0`` in the
+expiration time field for session cookies, as curl and Wget do.  Previously
+this field was left empty, and such lines were ignored by curl and Wget.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to