]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148849: Deprecate http.cookies.BaseCookie.js_output() (GH-148978)
authorkishorhange111 <101962449+kishorhange111@users.noreply.github.com>
Mon, 4 May 2026 09:51:17 +0000 (15:21 +0530)
committerGitHub <noreply@github.com>
Mon, 4 May 2026 09:51:17 +0000 (12:51 +0300)
Doc/deprecations/pending-removal-in-3.19.rst
Doc/library/http.cookies.rst
Doc/whatsnew/3.15.rst
Lib/http/cookies.py
Lib/test/test_http_cookies.py
Misc/NEWS.d/next/Library/2026-04-25-12-04-27.gh-issue-148849.Vk6yEW.rst [new file with mode: 0644]

index 25f9cba390de68ee5d1f1b9d853bf0337ac3a10c..044bb8a3934a2a76cff7359a354140fa93dfe58d 100644 (file)
@@ -22,3 +22,12 @@ Pending removal in Python 3.19
     supported depending on the backend implementation of hash functions.
     Prefer passing the initial data as a positional argument for maximum
     backwards compatibility.
+
+* :mod:`http.cookies`:
+
+  * :meth:`http.cookies.Morsel.js_output` is deprecated and will be
+    removed in Python 3.19.
+
+  * :meth:`http.cookies.BaseCookie.js_output` is deprecated and will be
+    removed in Python 3.19.
+
index 1122b30d29def0f3abbcaab71fd1e07f2975da64..4965c5fc3ba1d86f246f89e1cdada007164e7837 100644 (file)
@@ -107,6 +107,12 @@ Cookie Objects
 
    The meaning for *attrs* is the same as in :meth:`output`.
 
+   .. deprecated-removed:: 3.15 3.19
+      This method generates a JavaScript snippet to set cookies in the browser,
+      which is no longer considered a standard or recommended approach.
+      Use :meth:`~http.cookies.BaseCookie.output` instead to generate HTTP
+      headers.
+
 
 .. method:: BaseCookie.load(rawdata)
 
@@ -223,6 +229,12 @@ Morsel Objects
 
    The meaning for *attrs* is the same as in :meth:`output`.
 
+   .. deprecated-removed:: 3.15 3.19
+      This method generates a JavaScript snippet to set cookies in the browser,
+      which is no longer considered a standard or recommended approach.
+      Use :meth:`~http.cookies.Morsel.output` instead to generate HTTP
+      headers.
+
 
 .. method:: Morsel.OutputString(attrs=None)
 
index 586a1306d83c4ca9429c144ce153ccaf11511c12..02a3a11e0564d958431e8adcc93f948ccb5c3ffc 100644 (file)
@@ -1921,6 +1921,16 @@ New deprecations
     (Contributed by Bénédikt Tran in :gh:`134978`.)
 
 
+* :mod:`http.cookies`:
+
+  * :meth:`Morsel.js_output <http.cookies.Morsel.js_output>` and
+    :meth:`BaseCookie.js_output <http.cookies.BaseCookie.js_output>` are
+    deprecated and will be removed in Python 3.19. Use
+    :meth:`Morsel.output <http.cookies.Morsel.output>` or
+    :meth:`BaseCookie.output <http.cookies.BaseCookie.output>` instead.
+    (Contributed by kishorhange111 in :gh:`148849`.)
+
+
 * :mod:`re`:
 
   * :func:`re.match` and :meth:`re.Pattern.match` are now
index 5c5b14788dc2f09bf20a779ddbab8391da1af4dd..800a2c18e3fa41a7715c94e35198daabc61d4206 100644 (file)
@@ -132,6 +132,7 @@ Finis.
 import re
 import string
 import types
+lazy import warnings
 
 __all__ = ["CookieError", "BaseCookie", "SimpleCookie"]
 
@@ -390,7 +391,9 @@ class Morsel(dict):
     def __repr__(self):
         return '<%s: %s>' % (self.__class__.__name__, self.OutputString())
 
-    def js_output(self, attrs=None):
+
+    def _js_output(self, attrs=None):
+        """Internal implementation without deprecation warning."""
         import base64
         # Print javascript
         output_string = self.OutputString(attrs)
@@ -407,6 +410,14 @@ class Morsel(dict):
         </script>
         """ % (output_encoded,)
 
+    def js_output(self, attrs=None):
+        warnings._deprecated(
+            "http.cookies.Morsel.js_output",
+            message=warnings._DEPRECATED_MSG + "; use output() instead",
+            remove=(3, 19),
+        )
+        return self._js_output(attrs)
+
     def OutputString(self, attrs=None):
         # Build up our result
         #
@@ -541,10 +552,15 @@ class BaseCookie(dict):
 
     def js_output(self, attrs=None):
         """Return a string suitable for JavaScript."""
+        warnings._deprecated(
+            "http.cookies.BaseCookie.js_output",
+            message=warnings._DEPRECATED_MSG + "; use output() instead",
+            remove=(3, 19),
+        )
         result = []
         items = sorted(self.items())
         for key, value in items:
-            result.append(value.js_output(attrs))
+            result.append(value._js_output(attrs))
         return _nulljoin(result)
 
     def load(self, rawdata):
index 4884b07c95b9c50a29831316842be14a48e325f9..cde268e3241850906754b00b262b15abcc2bc2a2 100644 (file)
@@ -153,7 +153,8 @@ class CookieTests(unittest.TestCase):
         self.assertEqual(C.output(['path']),
             'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
         cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme; Version=1').decode('ascii')
-        self.assertEqual(C.js_output(), fr"""
+        with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
+            self.assertEqual(C.js_output(), fr"""
         <script type="text/javascript">
         <!-- begin hiding
         document.cookie = atob("{cookie_encoded}");
@@ -161,7 +162,8 @@ class CookieTests(unittest.TestCase):
         </script>
         """)
         cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme').decode('ascii')
-        self.assertEqual(C.js_output(['path']), fr"""
+        with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
+            self.assertEqual(C.js_output(['path']), fr"""
         <script type="text/javascript">
         <!-- begin hiding
         document.cookie = atob("{cookie_encoded}");
@@ -270,7 +272,8 @@ class CookieTests(unittest.TestCase):
         self.assertEqual(C.output(['path']),
                          'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
         expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1').decode('ascii')
-        self.assertEqual(C.js_output(), fr"""
+        with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
+            self.assertEqual(C.js_output(), fr"""
         <script type="text/javascript">
         <!-- begin hiding
         document.cookie = atob("{expected_encoded_cookie}");
@@ -278,7 +281,8 @@ class CookieTests(unittest.TestCase):
         </script>
         """)
         expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme').decode('ascii')
-        self.assertEqual(C.js_output(['path']), fr"""
+        with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
+            self.assertEqual(C.js_output(['path']), fr"""
         <script type="text/javascript">
         <!-- begin hiding
         document.cookie = atob("{expected_encoded_cookie}");
@@ -382,7 +386,8 @@ class MorselTests(unittest.TestCase):
         // end hiding -->
         </script>
         """ % (expected_encoded_cookie,)
-            self.assertEqual(M.js_output(), expected_js_output)
+            with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"):
+                self.assertEqual(M.js_output(), expected_js_output)
         for i in ["foo bar", "foo@bar"]:
             # Try some illegal characters
             self.assertRaises(cookies.CookieError,
@@ -650,7 +655,8 @@ class MorselTests(unittest.TestCase):
             cookie = cookies.SimpleCookie()
             cookie["cookie"] = morsel
             with self.assertRaises(cookies.CookieError):
-                cookie.js_output()
+                with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"):
+                    cookie.js_output()
 
             morsel = cookies.Morsel()
             morsel.set("key", "value", "coded-value")
@@ -658,8 +664,29 @@ class MorselTests(unittest.TestCase):
             cookie = cookies.SimpleCookie()
             cookie["cookie"] = morsel
             with self.assertRaises(cookies.CookieError):
-                cookie.js_output()
+                with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"):
+                    cookie.js_output()
 
+    def test_morsel_js_output_deprecated(self):
+        morsel = cookies.Morsel()
+        morsel.set("key", "value", "value")
+        with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output") as cm:
+            result = morsel.js_output()
+        self.assertEqual(cm.filename, __file__)
+        self.assertIn("document.cookie", result)
+
+
+    def test_basecookie_js_output_warns_once(self):
+        C = cookies.SimpleCookie()
+        C["key"] = "value"
+        with self.assertWarns(DeprecationWarning) as cm:
+            C.js_output()
+        deprecation_warnings = [
+            w for w in cm.warnings if issubclass(w.category, DeprecationWarning)
+        ]
+        self.assertEqual(len(deprecation_warnings), 1)
+        self.assertRegex(str(deprecation_warnings[0].message), r"BaseCookie\.js_output")
+        self.assertEqual(cm.filename, __file__)
 
 def load_tests(loader, tests, pattern):
     tests.addTest(doctest.DocTestSuite(cookies))
diff --git a/Misc/NEWS.d/next/Library/2026-04-25-12-04-27.gh-issue-148849.Vk6yEW.rst b/Misc/NEWS.d/next/Library/2026-04-25-12-04-27.gh-issue-148849.Vk6yEW.rst
new file mode 100644 (file)
index 0000000..9725d63
--- /dev/null
@@ -0,0 +1,4 @@
+Deprecate :meth:`http.cookies.Morsel.js_output` and
+:meth:`http.cookies.BaseCookie.js_output`, which will be removed in
+Python 3.19. Use :meth:`http.cookies.Morsel.output` or
+:meth:`http.cookies.BaseCookie.output` instead.