Skip to content

Commit 246fe14

Browse files
gh-148849: Deprecate http.cookies.BaseCookie.js_output() (GH-148978)
1 parent 0c6d2f6 commit 246fe14

6 files changed

Lines changed: 87 additions & 9 deletions

File tree

Doc/deprecations/pending-removal-in-3.19.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,12 @@ Pending removal in Python 3.19
2222
supported depending on the backend implementation of hash functions.
2323
Prefer passing the initial data as a positional argument for maximum
2424
backwards compatibility.
25+
26+
* :mod:`http.cookies`:
27+
28+
* :meth:`http.cookies.Morsel.js_output` is deprecated and will be
29+
removed in Python 3.19.
30+
31+
* :meth:`http.cookies.BaseCookie.js_output` is deprecated and will be
32+
removed in Python 3.19.
33+

Doc/library/http.cookies.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ Cookie Objects
107107

108108
The meaning for *attrs* is the same as in :meth:`output`.
109109

110+
.. deprecated-removed:: 3.15 3.19
111+
This method generates a JavaScript snippet to set cookies in the browser,
112+
which is no longer considered a standard or recommended approach.
113+
Use :meth:`~http.cookies.BaseCookie.output` instead to generate HTTP
114+
headers.
115+
110116

111117
.. method:: BaseCookie.load(rawdata)
112118

@@ -223,6 +229,12 @@ Morsel Objects
223229

224230
The meaning for *attrs* is the same as in :meth:`output`.
225231

232+
.. deprecated-removed:: 3.15 3.19
233+
This method generates a JavaScript snippet to set cookies in the browser,
234+
which is no longer considered a standard or recommended approach.
235+
Use :meth:`~http.cookies.Morsel.output` instead to generate HTTP
236+
headers.
237+
226238

227239
.. method:: Morsel.OutputString(attrs=None)
228240

Doc/whatsnew/3.15.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,6 +1921,16 @@ New deprecations
19211921
(Contributed by Bénédikt Tran in :gh:`134978`.)
19221922

19231923

1924+
* :mod:`http.cookies`:
1925+
1926+
* :meth:`Morsel.js_output <http.cookies.Morsel.js_output>` and
1927+
:meth:`BaseCookie.js_output <http.cookies.BaseCookie.js_output>` are
1928+
deprecated and will be removed in Python 3.19. Use
1929+
:meth:`Morsel.output <http.cookies.Morsel.output>` or
1930+
:meth:`BaseCookie.output <http.cookies.BaseCookie.output>` instead.
1931+
(Contributed by kishorhange111 in :gh:`148849`.)
1932+
1933+
19241934
* :mod:`re`:
19251935

19261936
* :func:`re.match` and :meth:`re.Pattern.match` are now

Lib/http/cookies.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
import re
133133
import string
134134
import types
135+
lazy import warnings
135136

136137
__all__ = ["CookieError", "BaseCookie", "SimpleCookie"]
137138

@@ -390,7 +391,9 @@ def output(self, attrs=None, header="Set-Cookie:"):
390391
def __repr__(self):
391392
return '<%s: %s>' % (self.__class__.__name__, self.OutputString())
392393

393-
def js_output(self, attrs=None):
394+
395+
def _js_output(self, attrs=None):
396+
"""Internal implementation without deprecation warning."""
394397
import base64
395398
# Print javascript
396399
output_string = self.OutputString(attrs)
@@ -407,6 +410,14 @@ def js_output(self, attrs=None):
407410
</script>
408411
""" % (output_encoded,)
409412

413+
def js_output(self, attrs=None):
414+
warnings._deprecated(
415+
"http.cookies.Morsel.js_output",
416+
message=warnings._DEPRECATED_MSG + "; use output() instead",
417+
remove=(3, 19),
418+
)
419+
return self._js_output(attrs)
420+
410421
def OutputString(self, attrs=None):
411422
# Build up our result
412423
#
@@ -541,10 +552,15 @@ def __repr__(self):
541552

542553
def js_output(self, attrs=None):
543554
"""Return a string suitable for JavaScript."""
555+
warnings._deprecated(
556+
"http.cookies.BaseCookie.js_output",
557+
message=warnings._DEPRECATED_MSG + "; use output() instead",
558+
remove=(3, 19),
559+
)
544560
result = []
545561
items = sorted(self.items())
546562
for key, value in items:
547-
result.append(value.js_output(attrs))
563+
result.append(value._js_output(attrs))
548564
return _nulljoin(result)
549565

550566
def load(self, rawdata):

Lib/test/test_http_cookies.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,17 @@ def test_load(self):
153153
self.assertEqual(C.output(['path']),
154154
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
155155
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme; Version=1').decode('ascii')
156-
self.assertEqual(C.js_output(), fr"""
156+
with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
157+
self.assertEqual(C.js_output(), fr"""
157158
<script type="text/javascript">
158159
<!-- begin hiding
159160
document.cookie = atob("{cookie_encoded}");
160161
// end hiding -->
161162
</script>
162163
""")
163164
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme').decode('ascii')
164-
self.assertEqual(C.js_output(['path']), fr"""
165+
with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
166+
self.assertEqual(C.js_output(['path']), fr"""
165167
<script type="text/javascript">
166168
<!-- begin hiding
167169
document.cookie = atob("{cookie_encoded}");
@@ -270,15 +272,17 @@ def test_quoted_meta(self):
270272
self.assertEqual(C.output(['path']),
271273
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
272274
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1').decode('ascii')
273-
self.assertEqual(C.js_output(), fr"""
275+
with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
276+
self.assertEqual(C.js_output(), fr"""
274277
<script type="text/javascript">
275278
<!-- begin hiding
276279
document.cookie = atob("{expected_encoded_cookie}");
277280
// end hiding -->
278281
</script>
279282
""")
280283
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme').decode('ascii')
281-
self.assertEqual(C.js_output(['path']), fr"""
284+
with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
285+
self.assertEqual(C.js_output(['path']), fr"""
282286
<script type="text/javascript">
283287
<!-- begin hiding
284288
document.cookie = atob("{expected_encoded_cookie}");
@@ -382,7 +386,8 @@ def test_setter(self):
382386
// end hiding -->
383387
</script>
384388
""" % (expected_encoded_cookie,)
385-
self.assertEqual(M.js_output(), expected_js_output)
389+
with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"):
390+
self.assertEqual(M.js_output(), expected_js_output)
386391
for i in ["foo bar", "foo@bar"]:
387392
# Try some illegal characters
388393
self.assertRaises(cookies.CookieError,
@@ -650,16 +655,38 @@ def test_control_characters_output(self):
650655
cookie = cookies.SimpleCookie()
651656
cookie["cookie"] = morsel
652657
with self.assertRaises(cookies.CookieError):
653-
cookie.js_output()
658+
with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"):
659+
cookie.js_output()
654660

655661
morsel = cookies.Morsel()
656662
morsel.set("key", "value", "coded-value")
657663
morsel._coded_value = c0 # Override private variable.
658664
cookie = cookies.SimpleCookie()
659665
cookie["cookie"] = morsel
660666
with self.assertRaises(cookies.CookieError):
661-
cookie.js_output()
667+
with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output"):
668+
cookie.js_output()
662669

670+
def test_morsel_js_output_deprecated(self):
671+
morsel = cookies.Morsel()
672+
morsel.set("key", "value", "value")
673+
with self.assertWarnsRegex(DeprecationWarning, r"Morsel\.js_output") as cm:
674+
result = morsel.js_output()
675+
self.assertEqual(cm.filename, __file__)
676+
self.assertIn("document.cookie", result)
677+
678+
679+
def test_basecookie_js_output_warns_once(self):
680+
C = cookies.SimpleCookie()
681+
C["key"] = "value"
682+
with self.assertWarns(DeprecationWarning) as cm:
683+
C.js_output()
684+
deprecation_warnings = [
685+
w for w in cm.warnings if issubclass(w.category, DeprecationWarning)
686+
]
687+
self.assertEqual(len(deprecation_warnings), 1)
688+
self.assertRegex(str(deprecation_warnings[0].message), r"BaseCookie\.js_output")
689+
self.assertEqual(cm.filename, __file__)
663690

664691
def load_tests(loader, tests, pattern):
665692
tests.addTest(doctest.DocTestSuite(cookies))
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Deprecate :meth:`http.cookies.Morsel.js_output` and
2+
:meth:`http.cookies.BaseCookie.js_output`, which will be removed in
3+
Python 3.19. Use :meth:`http.cookies.Morsel.output` or
4+
:meth:`http.cookies.BaseCookie.output` instead.

0 commit comments

Comments
 (0)