Skip to content

Commit 2373465

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Sync pre-release CPython 3.15 branch from GitHub (2026-05-27)
Summary: Imported python/cpython `3.15.0b1+` from upstream rev [`f216c89`](https://www.github.com/python/cpython/commit/f216c8963139bf858415e2d09cf938d3e29558df) (committed 2026-05-27 21:47:03+00:00). # Commit Info - Base: (`3.15.0b1+`) - [`528356e`](https://www.github.com/python/cpython/commit/528356eac0adf70fed0cf37dbb04c8fd529672d8) (commit date: 2026-05-26 19:46:16+00:00) - Imported: (`3.15.0b1+`) - [`f216c89`](https://www.github.com/python/cpython/commit/f216c8963139bf858415e2d09cf938d3e29558df) (commit date: 2026-05-27 21:47:03+00:00) # Noteworthy file changes - Low-signal files (2 added) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GKATbSjgTT7ZlgMKAIdnxsR05dULbr0LAAAz Differential Revision: D106605961 fbshipit-source-id: f2f8e2f0db848426a9e4cf3817f91860ae84965c
1 parent a703f19 commit 2373465

8 files changed

Lines changed: 172 additions & 81 deletions

File tree

Doc/reference/compound_stmts.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ The match statement is used for pattern matching. Syntax:
620620
match_stmt: 'match' `subject_expr` ":" NEWLINE INDENT `case_block`+ DEDENT
621621
subject_expr: `flexible_expression` "," [`flexible_expression_list` [',']]
622622
: | `assignment_expression`
623-
case_block: 'case' `patterns` [`guard`] ":" `!block`
623+
case_block: 'case' `patterns` [`guard`] ":" `suite`
624624

625625
.. note::
626626
This section uses single quotes to denote

Lib/test/test_perf_profiler.py

Lines changed: 104 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,21 @@ def supports_trampoline_profiling():
3434
raise unittest.SkipTest("perf trampoline profiling not supported")
3535

3636

37+
def _perf_env(**env_vars):
38+
env = os.environ.copy()
39+
# Keep perf's output stable regardless of the builder's perf config.
40+
env.update(
41+
{
42+
"DEBUGINFOD_URLS": "",
43+
"PERF_CONFIG": os.devnull,
44+
}
45+
)
46+
if env_vars:
47+
env.update(env_vars)
48+
env["PYTHON_JIT"] = "0"
49+
return env
50+
51+
3752
class TestPerfTrampoline(unittest.TestCase):
3853
def setUp(self):
3954
super().setUp()
@@ -63,13 +78,12 @@ def baz():
6378
"""
6479
with temp_dir() as script_dir:
6580
script = make_script(script_dir, "perftest", code)
66-
env = {**os.environ, "PYTHON_JIT": "0"}
6781
with subprocess.Popen(
6882
[sys.executable, "-Xperf", script],
6983
text=True,
7084
stderr=subprocess.PIPE,
7185
stdout=subprocess.PIPE,
72-
env=env,
86+
env=_perf_env(),
7387
) as process:
7488
stdout, stderr = process.communicate()
7589

@@ -132,13 +146,12 @@ def baz():
132146
"""
133147
with temp_dir() as script_dir:
134148
script = make_script(script_dir, "perftest", code)
135-
env = {**os.environ, "PYTHON_JIT": "0"}
136149
with subprocess.Popen(
137150
[sys.executable, "-Xperf", script],
138151
text=True,
139152
stderr=subprocess.PIPE,
140153
stdout=subprocess.PIPE,
141-
env=env,
154+
env=_perf_env(),
142155
) as process:
143156
stdout, stderr = process.communicate()
144157

@@ -198,13 +211,12 @@ def test_trampoline_works_after_fork_with_many_code_objects(self):
198211
"""
199212
with temp_dir() as script_dir:
200213
script = make_script(script_dir, "perftest", code)
201-
env = {**os.environ, "PYTHON_JIT": "0"}
202214
with subprocess.Popen(
203215
[sys.executable, "-Xperf", script],
204216
text=True,
205217
stderr=subprocess.PIPE,
206218
stdout=subprocess.PIPE,
207-
env=env,
219+
env=_perf_env(),
208220
) as process:
209221
stdout, stderr = process.communicate()
210222

@@ -242,13 +254,12 @@ def baz():
242254
code = set_eval_hook + code
243255
with temp_dir() as script_dir:
244256
script = make_script(script_dir, "perftest", code)
245-
env = {**os.environ, "PYTHON_JIT": "0"}
246257
with subprocess.Popen(
247258
[sys.executable, script],
248259
text=True,
249260
stderr=subprocess.PIPE,
250261
stdout=subprocess.PIPE,
251-
env=env,
262+
env=_perf_env(),
252263
) as process:
253264
stdout, stderr = process.communicate()
254265

@@ -345,9 +356,12 @@ def perf_command_works():
345356
"-c",
346357
'print("hello")',
347358
)
348-
env = {**os.environ, "PYTHON_JIT": "0"}
349359
stdout = subprocess.check_output(
350-
cmd, cwd=script_dir, text=True, stderr=subprocess.STDOUT, env=env
360+
cmd,
361+
cwd=script_dir,
362+
text=True,
363+
stderr=subprocess.STDOUT,
364+
env=_perf_env(),
351365
)
352366
except (subprocess.SubprocessError, OSError):
353367
return False
@@ -359,43 +373,49 @@ def perf_command_works():
359373

360374

361375
def run_perf(cwd, *args, use_jit=False, **env_vars):
362-
env = os.environ.copy()
363-
if env_vars:
364-
env.update(env_vars)
365-
env["PYTHON_JIT"] = "0"
376+
env = _perf_env(**env_vars)
366377
output_file = cwd + "/perf_output.perf"
367-
if not use_jit:
368-
base_cmd = (
369-
"perf",
370-
"record",
371-
"--no-buildid",
372-
"--no-buildid-cache",
373-
"-g",
374-
"--call-graph=fp",
375-
"-o", output_file,
376-
"--"
377-
)
378+
base_cmd = [
379+
"perf",
380+
"record",
381+
"--no-buildid",
382+
"--no-buildid-cache",
383+
"-g",
384+
"--call-graph=dwarf,65528" if use_jit else "--call-graph=fp",
385+
]
386+
if use_jit:
387+
perf_commands = []
388+
# Some builders have low perf_event_mlock_kb limits.
389+
mmap_sizes = ("4M", "2M", "1M", "512K", "256K", "128K", None)
390+
for mmap_size in mmap_sizes:
391+
command = base_cmd.copy()
392+
if mmap_size is not None:
393+
command += ["-F99", "-k1", "-m", mmap_size]
394+
else:
395+
command += ["-F99", "-k1"]
396+
command += ["-o", output_file, "--"]
397+
perf_commands.append(command)
378398
else:
379-
base_cmd = (
380-
"perf",
381-
"record",
382-
"--no-buildid",
383-
"--no-buildid-cache",
384-
"-g",
385-
"--call-graph=dwarf,65528",
386-
"-F99",
387-
"-k1",
388-
"-o",
389-
output_file,
390-
"--",
399+
perf_commands = [base_cmd + ["-o", output_file, "--"]]
400+
401+
mmap_pages_error = "try again with a smaller value of -m/--mmap_pages"
402+
for index, base_cmd in enumerate(perf_commands):
403+
proc = subprocess.run(
404+
base_cmd + list(args),
405+
stdout=subprocess.PIPE,
406+
stderr=subprocess.PIPE,
407+
env=env,
408+
text=True,
391409
)
392-
proc = subprocess.run(
393-
base_cmd + args,
394-
stdout=subprocess.PIPE,
395-
stderr=subprocess.PIPE,
396-
env=env,
397-
text=True,
398-
)
410+
if (
411+
proc.returncode
412+
and use_jit
413+
and index != len(perf_commands) - 1
414+
and mmap_pages_error in proc.stderr
415+
):
416+
continue
417+
break
418+
399419
if proc.returncode:
400420
print(proc.stderr, file=sys.stderr)
401421
raise ValueError(f"Perf failed with return code {proc.returncode}")
@@ -425,54 +445,77 @@ def run_perf(cwd, *args, use_jit=False, **env_vars):
425445

426446

427447
class TestPerfProfilerMixin:
428-
def run_perf(self, script_dir, perf_mode, script):
448+
PERF_CAPTURE_ATTEMPTS = 3
449+
450+
def run_perf(self, script_dir, script, activate_trampoline=True):
429451
raise NotImplementedError()
430452

453+
def run_perf_with_retries(
454+
self, script_dir, script, expected_symbols=(), activate_trampoline=True
455+
):
456+
stdout = stderr = ""
457+
for _ in range(self.PERF_CAPTURE_ATTEMPTS):
458+
stdout, stderr = self.run_perf(
459+
script_dir, script, activate_trampoline=activate_trampoline
460+
)
461+
if activate_trampoline and any(
462+
symbol not in stdout for symbol in expected_symbols
463+
):
464+
continue
465+
break
466+
return stdout, stderr
467+
431468
def test_python_calls_appear_in_the_stack_if_perf_activated(self):
432469
with temp_dir() as script_dir:
433470
code = """if 1:
471+
from itertools import repeat
472+
434473
def foo(n):
435-
x = 0
436-
for i in range(n):
437-
x += i
474+
for _ in repeat(None, n):
475+
pass
438476
439477
def bar(n):
440478
foo(n)
441479
442480
def baz(n):
443481
bar(n)
444482
445-
baz(10000000)
483+
baz(40000000)
446484
"""
447485
script = make_script(script_dir, "perftest", code)
448-
stdout, stderr = self.run_perf(script_dir, script)
449-
self.assertEqual(stderr, "")
486+
expected_symbols = [
487+
f"py::foo:{script}",
488+
f"py::bar:{script}",
489+
f"py::baz:{script}",
490+
]
491+
stdout, _ = self.run_perf_with_retries(
492+
script_dir, script, expected_symbols
493+
)
450494

451-
self.assertIn(f"py::foo:{script}", stdout)
452-
self.assertIn(f"py::bar:{script}", stdout)
453-
self.assertIn(f"py::baz:{script}", stdout)
495+
for expected_symbol in expected_symbols:
496+
self.assertIn(expected_symbol, stdout)
454497

455498
def test_python_calls_do_not_appear_in_the_stack_if_perf_deactivated(self):
456499
with temp_dir() as script_dir:
457500
code = """if 1:
501+
from itertools import repeat
502+
458503
def foo(n):
459-
x = 0
460-
for i in range(n):
461-
x += i
504+
for _ in repeat(None, n):
505+
pass
462506
463507
def bar(n):
464508
foo(n)
465509
466510
def baz(n):
467511
bar(n)
468512
469-
baz(10000000)
513+
baz(40000000)
470514
"""
471515
script = make_script(script_dir, "perftest", code)
472-
stdout, stderr = self.run_perf(
516+
stdout, _ = self.run_perf_with_retries(
473517
script_dir, script, activate_trampoline=False
474518
)
475-
self.assertEqual(stderr, "")
476519

477520
self.assertNotIn(f"py::foo:{script}", stdout)
478521
self.assertNotIn(f"py::bar:{script}", stdout)
@@ -542,13 +585,12 @@ def compile_trampolines_for_all_functions():
542585

543586
with temp_dir() as script_dir:
544587
script = make_script(script_dir, "perftest", code)
545-
env = {**os.environ, "PYTHON_JIT": "0"}
546588
with subprocess.Popen(
547589
[sys.executable, "-Xperf", script],
548590
universal_newlines=True,
549591
stderr=subprocess.PIPE,
550592
stdout=subprocess.PIPE,
551-
env=env,
593+
env=_perf_env(),
552594
) as process:
553595
stdout, stderr = process.communicate()
554596

Lib/test/test_xml_etree.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3657,6 +3657,32 @@ def test_basic(self):
36573657
doc = ET.XML("<root>a&amp;<sub>b&amp;</sub>c&amp;</root>")
36583658
self.assertEqual(''.join(doc.itertext()), 'a&b&c&')
36593659

3660+
def test_comment(self):
3661+
e = ET.Element('root')
3662+
e.text = 'before'
3663+
comment = ET.Comment('comment')
3664+
self.assertEqual(comment.text, 'comment')
3665+
comment.tail = 'after'
3666+
e.append(comment)
3667+
self.assertEqual(''.join(e.itertext()), 'beforeafter')
3668+
self.assertEqual(list(e.iter()), [e, comment])
3669+
self.assertEqual(list(e.iter('root')), [e])
3670+
self.assertEqual(''.join(comment.itertext()), '')
3671+
self.assertEqual(list(comment.iter()), [comment])
3672+
3673+
def test_processinginstruction(self):
3674+
e = ET.Element('root')
3675+
e.text = 'before'
3676+
pi = ET.PI('test', 'instruction')
3677+
self.assertEqual(pi.text, 'test instruction')
3678+
pi.tail = 'after'
3679+
e.append(pi)
3680+
self.assertEqual(''.join(e.itertext()), 'beforeafter')
3681+
self.assertEqual(list(e.iter()), [e, pi])
3682+
self.assertEqual(list(e.iter('root')), [e])
3683+
self.assertEqual(''.join(pi.itertext()), '')
3684+
self.assertEqual(list(pi.iter()), [pi])
3685+
36603686
def test_corners(self):
36613687
# single root, no subelements
36623688
a = ET.Element('a')

Lib/test/test_zipfile/test_core.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3640,29 +3640,23 @@ def test_read_with_unsuitable_metadata_encoding(self):
36403640

36413641
def test_read_after_append(self):
36423642
newname = '\u56db' # Han 'four'
3643-
expected_names = [name.encode('shift_jis').decode('cp437')
3644-
for name in self.file_names[:2]] + self.file_names[2:]
3645-
expected_names.append(newname)
3646-
expected_content = (*self.file_content, b"newcontent")
3643+
newname2 = 'fünf' # representable in cp437, but still stored as UTF-8
3644+
expected_names = [*self.file_names, newname, newname2]
3645+
mojibake_expected_names = [name.encode('shift_jis').decode('cp437')
3646+
if i < 2 else name
3647+
for i, name in enumerate(expected_names)]
3648+
expected_content = (*self.file_content, b"newcontent", b"newcontent2")
36473649

36483650
with zipfile.ZipFile(TESTFN, "a") as zipfp:
36493651
zipfp.writestr(newname, "newcontent")
3650-
self.assertEqual(sorted(zipfp.namelist()), sorted(expected_names))
3652+
zipfp.writestr(newname2, "newcontent2")
3653+
self.assertEqual(sorted(zipfp.namelist()), sorted(mojibake_expected_names))
36513654

36523655
with zipfile.ZipFile(TESTFN, "r") as zipfp:
3653-
self._test_read(zipfp, expected_names, expected_content)
3656+
self._test_read(zipfp, mojibake_expected_names, expected_content)
36543657

36553658
with zipfile.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp:
3656-
self.assertEqual(sorted(zipfp.namelist()), sorted(expected_names))
3657-
for i, (name, content) in enumerate(zip(expected_names, expected_content)):
3658-
info = zipfp.getinfo(name)
3659-
self.assertEqual(info.filename, name)
3660-
self.assertEqual(info.file_size, len(content))
3661-
if i < 2:
3662-
with self.assertRaises(zipfile.BadZipFile):
3663-
zipfp.read(name)
3664-
else:
3665-
self.assertEqual(zipfp.read(name), content)
3659+
self._test_read(zipfp, expected_names, expected_content)
36663660

36673661
def test_write_with_metadata_encoding(self):
36683662
ZF = zipfile.ZipFile
@@ -3671,6 +3665,20 @@ def test_write_with_metadata_encoding(self):
36713665
"^metadata_encoding is only"):
36723666
ZF("nonesuch.zip", mode, metadata_encoding="shift_jis")
36733667

3668+
def test_add_comment(self):
3669+
with zipfile.ZipFile(TESTFN, "r") as zipfp:
3670+
mojibake_expected_names = zipfp.namelist()
3671+
3672+
with zipfile.ZipFile(TESTFN, "a") as zipfp:
3673+
zipfp.comment = b'comment'
3674+
self.assertEqual(zipfp.namelist(), mojibake_expected_names)
3675+
3676+
with zipfile.ZipFile(TESTFN, "r") as zipfp:
3677+
self._test_read(zipfp, mojibake_expected_names, self.file_content)
3678+
3679+
with zipfile.ZipFile(TESTFN, "r", metadata_encoding='shift_jis') as zipfp:
3680+
self._test_read(zipfp, self.file_names, self.file_content)
3681+
36743682
def test_cli_with_metadata_encoding(self):
36753683
errmsg = "Non-conforming encodings not supported with -c."
36763684
args = ["--metadata-encoding=shift_jis", "-c", "nonesuch", "nonesuch"]

0 commit comments

Comments
 (0)