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.