Skip to content

Commit 9f2ed68

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Sync pre-release CPython 3.15 branch from GitHub (2026-05-14)
Summary: Imported python/cpython `3.15.0b1+` from upstream rev [`ed27363`](https://www.github.com/python/cpython/commit/ed27363ddf340577afd5b7b67e604489f5ee7aa3) (committed 2026-05-14 22:38:11+00:00). # Commit Info Base: (`3.15.0b1+`) - [`894ec10`](https://www.github.com/python/cpython/commit/894ec10b56d93743e082ac9569abf896e082a919) (commit date: 2026-05-13 23:34:59+00:00) Imported: (`3.15.0b1+`) - [`ed27363`](https://www.github.com/python/cpython/commit/ed27363ddf340577afd5b7b67e604489f5ee7aa3) (commit date: 2026-05-14 22:38:11+00:00) # Noteworthy file changes - Low-signal files (2 added, 1 removed) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GJCoqynv7zAJn2oDAMmhoL-pMrp5br0LAAAz Differential Revision: D105281527 fbshipit-source-id: 8ca23de3bcb755ea8933128463d767328f3437fe
1 parent 1fb9441 commit 9f2ed68

18 files changed

Lines changed: 144 additions & 67 deletions

Doc/reference/compound_stmts.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -618,8 +618,8 @@ The match statement is used for pattern matching. Syntax:
618618

619619
.. productionlist:: python-grammar
620620
match_stmt: 'match' `subject_expr` ":" NEWLINE INDENT `case_block`+ DEDENT
621-
subject_expr: `!star_named_expression` "," `!star_named_expressions`?
622-
: | `!named_expression`
621+
subject_expr: `flexible_expression` "," [`flexible_expression_list` [',']]
622+
: | `assignment_expression`
623623
case_block: 'case' `patterns` [`guard`] ":" `!block`
624624

625625
.. note::
@@ -709,7 +709,7 @@ Guards
709709
.. index:: ! guard
710710

711711
.. productionlist:: python-grammar
712-
guard: "if" `!named_expression`
712+
guard: "if" `assignment_expression`
713713

714714
A ``guard`` (which is part of the ``case``) must succeed for code inside
715715
the ``case`` block to execute. It takes the form: :keyword:`if` followed by an

Lib/http/cookies.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,21 +391,20 @@ def output(self, attrs=None, header="Set-Cookie:"):
391391
def __repr__(self):
392392
return '<%s: %s>' % (self.__class__.__name__, self.OutputString())
393393

394-
395394
def _js_output(self, attrs=None):
396395
"""Internal implementation without deprecation warning."""
397-
import base64
396+
import urllib.parse
398397
# Print javascript
399398
output_string = self.OutputString(attrs)
400399
if _has_control_character(output_string):
401400
raise CookieError("Control characters are not allowed in cookies")
402401
# Base64-encode value to avoid template
403402
# injection in cookie values.
404-
output_encoded = base64.b64encode(output_string.encode('utf-8')).decode("ascii")
403+
output_encoded = urllib.parse.quote(output_string, safe='', encoding='utf-8')
405404
return """
406405
<script type="text/javascript">
407406
<!-- begin hiding
408-
document.cookie = atob(\"%s\");
407+
document.cookie = decodeURIComponent(\"%s\");
409408
// end hiding -->
410409
</script>
411410
""" % (output_encoded,)

Lib/test/test_http_cookies.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Simple test suite for http/cookies.py
2-
import base64
32
import copy
43
import unittest
54
import doctest
65
from http import cookies
76
import pickle
87
from test import support
8+
import urllib.parse
99

1010

1111
class CookieTests(unittest.TestCase):
@@ -152,21 +152,21 @@ def test_load(self):
152152

153153
self.assertEqual(C.output(['path']),
154154
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
155-
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme; Version=1').decode('ascii')
155+
cookie_encoded = urllib.parse.quote('Customer="WILE_E_COYOTE"; Path=/acme; Version=1', safe='', encoding='utf-8')
156156
with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
157157
self.assertEqual(C.js_output(), fr"""
158158
<script type="text/javascript">
159159
<!-- begin hiding
160-
document.cookie = atob("{cookie_encoded}");
160+
document.cookie = decodeURIComponent("{cookie_encoded}");
161161
// end hiding -->
162162
</script>
163163
""")
164-
cookie_encoded = base64.b64encode(b'Customer="WILE_E_COYOTE"; Path=/acme').decode('ascii')
164+
cookie_encoded = urllib.parse.quote('Customer="WILE_E_COYOTE"; Path=/acme', safe='', encoding='utf-8')
165165
with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
166166
self.assertEqual(C.js_output(['path']), fr"""
167167
<script type="text/javascript">
168168
<!-- begin hiding
169-
document.cookie = atob("{cookie_encoded}");
169+
document.cookie = decodeURIComponent("{cookie_encoded}");
170170
// end hiding -->
171171
</script>
172172
""")
@@ -271,21 +271,21 @@ def test_quoted_meta(self):
271271

272272
self.assertEqual(C.output(['path']),
273273
'Set-Cookie: Customer="WILE_E_COYOTE"; Path=/acme')
274-
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1').decode('ascii')
274+
expected_encoded_cookie = urllib.parse.quote('Customer=\"WILE_E_COYOTE\"; Path=/acme; Version=1', safe='', encoding='utf-8')
275275
with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
276276
self.assertEqual(C.js_output(), fr"""
277277
<script type="text/javascript">
278278
<!-- begin hiding
279-
document.cookie = atob("{expected_encoded_cookie}");
279+
document.cookie = decodeURIComponent("{expected_encoded_cookie}");
280280
// end hiding -->
281281
</script>
282282
""")
283-
expected_encoded_cookie = base64.b64encode(b'Customer=\"WILE_E_COYOTE\"; Path=/acme').decode('ascii')
283+
expected_encoded_cookie = urllib.parse.quote('Customer=\"WILE_E_COYOTE\"; Path=/acme', safe='', encoding='utf-8')
284284
with self.assertWarnsRegex(DeprecationWarning, r"BaseCookie\.js_output"):
285285
self.assertEqual(C.js_output(['path']), fr"""
286286
<script type="text/javascript">
287287
<!-- begin hiding
288-
document.cookie = atob("{expected_encoded_cookie}");
288+
document.cookie = decodeURIComponent("{expected_encoded_cookie}");
289289
// end hiding -->
290290
</script>
291291
""")
@@ -376,13 +376,14 @@ def test_setter(self):
376376
self.assertEqual(
377377
M.output(),
378378
"Set-Cookie: %s=%s; Path=/foo" % (i, "%s_coded_val" % i))
379-
expected_encoded_cookie = base64.b64encode(
380-
("%s=%s; Path=/foo" % (i, "%s_coded_val" % i)).encode("ascii")
381-
).decode('ascii')
379+
expected_encoded_cookie = urllib.parse.quote(
380+
"%s=%s; Path=/foo" % (i, "%s_coded_val" % i),
381+
safe='', encoding='utf-8',
382+
)
382383
expected_js_output = """
383384
<script type="text/javascript">
384385
<!-- begin hiding
385-
document.cookie = atob("%s");
386+
document.cookie = decodeURIComponent("%s");
386387
// end hiding -->
387388
</script>
388389
""" % (expected_encoded_cookie,)

Lib/test/test_pyexpat.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def test_parse_again(self):
289289
'mac-roman', 'mac-turkish',
290290
'koi8-r', 'koi8-t', 'koi8-u', 'kz1048', 'ptcp154',
291291
])
292-
def test_supported_ecodings(self, encoding):
292+
def test_supported_encodings(self, encoding):
293293
out = self.Outputter()
294294
parser = expat.ParserCreate()
295295
self._hookup_callbacks(parser, out)
@@ -308,7 +308,7 @@ def test_supported_ecodings(self, encoding):
308308
'UTF-8', 'utf-8', 'utf-16', 'utf-16le', 'utf-16be',
309309
'koi8-u', 'cp1125', 'cp1251', 'iso8859-5', 'mac-cyrillic',
310310
])
311-
def test_supported_ecodings2(self, encoding):
311+
def test_supported_encodings2(self, encoding):
312312
out = self.Outputter()
313313
parser = expat.ParserCreate()
314314
self._hookup_callbacks(parser, out)
@@ -334,14 +334,54 @@ def test_supported_ecodings2(self, encoding):
334334
"johab",
335335
"Shift_JIS", "Shift_JIS-2004", "Shift_JISX0213",
336336
])
337-
def test_unsupportes_ecodings(self, encoding):
337+
def test_unsupported_encodings(self, encoding):
338338
parser = expat.ParserCreate()
339339
data = (f'<?xml version="1.0" encoding="{encoding}"?>\n'
340340
'<root></root>').encode(encoding)
341341
with self.assertRaises(ValueError):
342342
parser.Parse(data, True)
343343

344-
def test_unknown_ecoding(self):
344+
parser = expat.ParserCreate()
345+
data = (f'<?xml version="1.0" encoding="{encoding}"?>\n'
346+
'<root></root>').encode()
347+
with self.assertRaises(ValueError):
348+
parser.Parse(data, True)
349+
350+
@support.subTests('encoding', [
351+
'cp037', 'cp273', 'cp424', 'cp500', 'cp864', 'cp875',
352+
'cp1026', 'cp1140',
353+
'mac_arabic', 'mac_farsi',
354+
])
355+
def test_incompatible_encodings(self, encoding):
356+
parser = expat.ParserCreate()
357+
data = (f'<?xml version="1.0" encoding="{encoding}"?>\n'
358+
'<root></root>').encode(encoding)
359+
with self.assertRaises(expat.ExpatError):
360+
parser.Parse(data, True)
361+
362+
parser = expat.ParserCreate()
363+
data = (f'<?xml version="1.0" encoding="{encoding}"?>\n'
364+
'<root></root>').encode()
365+
with self.assertRaisesRegex(expat.ExpatError, 'unknown encoding'):
366+
parser.Parse(data, True)
367+
368+
@support.subTests('encoding', [
369+
'hex_codec', 'rot_13',
370+
])
371+
def test_non_text_encodings(self, encoding):
372+
parser = expat.ParserCreate()
373+
data = (f'<?xml version="1.0" encoding="{encoding}"?>\n'
374+
'<root></root>').encode()
375+
with self.assertRaises(LookupError):
376+
parser.Parse(data, True)
377+
378+
def test_undefined_encoding(self):
379+
parser = expat.ParserCreate()
380+
data = b'<?xml version="1.0" encoding="undefined"?>\n<root></root>'
381+
with self.assertRaises(UnicodeError):
382+
parser.Parse(data, True)
383+
384+
def test_unknown_encoding(self):
345385
parser = expat.ParserCreate()
346386
data = b'<?xml version="1.0" encoding="xyz"?>\n<root></root>'
347387
with self.assertRaises(LookupError):

Lib/test/test_tcl.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ def test_eval_null_in_result(self):
5454

5555
def test_eval_surrogates_in_result(self):
5656
tcl = self.interp
57-
self.assertEqual(tcl.eval(r'set a "<\ud83d\udcbb>"'), '<\U0001f4bb>')
57+
result = tcl.eval(r'set a "<\ud83d\udcbb>"')
58+
if sys.platform == 'win32':
59+
self.assertEqual('<\ud83d\udcbb>', result)
60+
else:
61+
self.assertEqual('<\U0001f4bb>', result)
5862

5963
def testEvalException(self):
6064
tcl = self.interp
@@ -289,7 +293,11 @@ def test_evalfile_surrogates_in_result(self):
289293
set b "<\\ud83d\\udcbb>"
290294
""")
291295
tcl.evalfile(filename)
292-
self.assertEqual(tcl.eval('set b'), '<\U0001f4bb>')
296+
result = tcl.eval('set b')
297+
if sys.platform == 'win32':
298+
self.assertEqual('<\ud83d\udcbb>', result)
299+
else:
300+
self.assertEqual('<\U0001f4bb>', result)
293301

294302
def testEvalFileException(self):
295303
tcl = self.interp

Misc/NEWS.d/next/Library/2026-05-01-16-45-31.gh-issue-149231.x2nBEE.rst

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Update bundled `libexpat <https://libexpat.github.io/>`_ to version 2.8.1
2+
for the fix for :cve:`2026-45186`.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Updated Windows builds to use Tcl/Tk 9.0.3.

Misc/externals.spdx.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,46 +108,46 @@
108108
"versionInfo": "3.50.4.0"
109109
},
110110
{
111-
"SPDXID": "SPDXRef-PACKAGE-tcl-core",
111+
"SPDXID": "SPDXRef-PACKAGE-tcl",
112112
"checksums": [
113113
{
114114
"algorithm": "SHA256",
115-
"checksumValue": "4c23f0dd3efcbe6f3a22c503a68d147617bb30c4f5290f1eb3eaacf0b460440b"
115+
"checksumValue": "7a1d1f3a2b8f4484a9c2a027a157963c18f85a81785e85fcb5d1e3df6b6a4fd4"
116116
}
117117
],
118-
"downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/tcl-core-8.6.15.0.tar.gz",
118+
"downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/tcl-9.0.3.0.tar.gz",
119119
"externalRefs": [
120120
{
121121
"referenceCategory": "SECURITY",
122-
"referenceLocator": "cpe:2.3:a:tcl_tk:tcl_tk:8.6.15.0:*:*:*:*:*:*:*",
122+
"referenceLocator": "cpe:2.3:a:tcl_tk:tcl_tk:9.0.3.0:*:*:*:*:*:*:*",
123123
"referenceType": "cpe23Type"
124124
}
125125
],
126126
"licenseConcluded": "NOASSERTION",
127-
"name": "tcl-core",
127+
"name": "tcl",
128128
"primaryPackagePurpose": "SOURCE",
129-
"versionInfo": "8.6.15.0"
129+
"versionInfo": "9.0.3.0"
130130
},
131131
{
132132
"SPDXID": "SPDXRef-PACKAGE-tk",
133133
"checksums": [
134134
{
135135
"algorithm": "SHA256",
136-
"checksumValue": "0ae56d39bca92865f338529557a1e56d110594184b6dc5a91339c5675751e264"
136+
"checksumValue": "54fb59df12c489c6264f5b7d3d7444b150d1e3d6561fd59cdb11483440cec000"
137137
}
138138
],
139-
"downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/tk-8.6.15.0.tar.gz",
139+
"downloadLocation": "https://github.com/python/cpython-source-deps/archive/refs/tags/tk-9.0.3.1.tar.gz",
140140
"externalRefs": [
141141
{
142142
"referenceCategory": "SECURITY",
143-
"referenceLocator": "cpe:2.3:a:tcl_tk:tcl_tk:8.6.15.0:*:*:*:*:*:*:*",
143+
"referenceLocator": "cpe:2.3:a:tcl_tk:tcl_tk:9.0.3.1:*:*:*:*:*:*:*",
144144
"referenceType": "cpe23Type"
145145
}
146146
],
147147
"licenseConcluded": "NOASSERTION",
148148
"name": "tk",
149149
"primaryPackagePurpose": "SOURCE",
150-
"versionInfo": "8.6.15.0"
150+
"versionInfo": "9.0.3.1"
151151
},
152152
{
153153
"SPDXID": "SPDXRef-PACKAGE-xz",

Misc/sbom.spdx.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)