Skip to content

Commit c99a69a

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Import CPython 3.14.5+ stable branch (2026-05-20)
Summary: Imported python/cpython `3.14.5+` from upstream rev [`25092e8`](https://www.github.com/python/cpython/commit/25092e822a06140796365f8fea188707265eb4d3) (committed 2026-05-20 19:45:25+00:00). # Commit Info Base: (`3.14.5+`) - [`8e13025`](https://www.github.com/python/cpython/commit/8e13025747e1ca72e86d1f35637123f9c306f0cb) (commit date: 2026-05-19 08:43:57+00:00) Imported: (`3.14.5+`) - [`25092e8`](https://www.github.com/python/cpython/commit/25092e822a06140796365f8fea188707265eb4d3) (commit date: 2026-05-20 19:45:25+00:00) # Noteworthy file changes - Low-signal files (6 added) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GPZTuCmFZARuvQwGAEisSr08KMVnbr0LAAAz Reviewed By: itamaro Differential Revision: D105945865 fbshipit-source-id: 656c0d41062f0a8ad11e1f12fc65c8fd09becaed
1 parent 8395a75 commit c99a69a

20 files changed

Lines changed: 252 additions & 75 deletions

Doc/library/stdtypes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,8 @@ expression support in the :mod:`re` module).
27432743
The *chars* argument is not a prefix or suffix; rather, all combinations of its
27442744
values are stripped.
27452745

2746+
Whitespace characters are defined by :meth:`str.isspace`.
2747+
27462748
For example:
27472749

27482750
.. doctest::

Doc/library/string.rst

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,9 @@ of a number respectively. It can be one of the following:
472472
| | this option is not supported. |
473473
+---------+----------------------------------------------------------+
474474

475-
For a locale aware separator, use the ``'n'`` presentation type instead.
475+
For a locale-aware separator, use the ``'n'``
476+
:ref:`float presentation type <n-format-float>` or
477+
:ref:`integer presentation type <n-format-integer>` instead.
476478

477479
.. versionchanged:: 3.1
478480
Added the ``','`` option (see also :pep:`378`).
@@ -518,9 +520,14 @@ The available integer presentation types are:
518520
| | In case ``'#'`` is specified, the prefix ``'0x'`` will |
519521
| | be upper-cased to ``'0X'`` as well. |
520522
+---------+----------------------------------------------------------+
521-
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
523+
| ``'n'`` | .. _n-format-integer: |
524+
| | |
525+
| | Number. This is the same as ``'d'``, except that it uses |
522526
| | the current locale setting to insert the appropriate |
523-
| | digit group separators. |
527+
| | digit group separators. Note that the default locale is |
528+
| | not the system locale. Depending on your use case, you |
529+
| | may wish to set :const:`~locale.LC_NUMERIC` with |
530+
| | :func:`locale.setlocale` before using ``'n'``. |
524531
+---------+----------------------------------------------------------+
525532
| None | The same as ``'d'``. |
526533
+---------+----------------------------------------------------------+
@@ -603,10 +610,15 @@ The available presentation types for :class:`float` and
603610
| | ``'E'`` if the number gets too large. The |
604611
| | representations of infinity and NaN are uppercased, too. |
605612
+---------+----------------------------------------------------------+
606-
| ``'n'`` | Number. This is the same as ``'g'``, except that it uses |
613+
| ``'n'`` | .. _n-format-float: |
614+
| | |
615+
| | Number. This is the same as ``'g'``, except that it uses |
607616
| | the current locale setting to insert the appropriate |
608-
| | digit group separators |
609-
| | for the integral part of a number. |
617+
| | digit group separators for the integral part of a |
618+
| | number. Note that the default locale is not the system |
619+
| | locale. Depending on your use case, you may wish to set |
620+
| | :const:`~locale.LC_NUMERIC` with |
621+
| | :func:`locale.setlocale` before using ``'n'``. |
610622
+---------+----------------------------------------------------------+
611623
| ``'%'`` | Percentage. Multiplies the number by 100 and displays |
612624
| | in fixed (``'f'``) format, followed by a percent sign. |

Lib/test/test_free_threading/test_iteration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
NUMITEMS = 1000
1313
NUMTHREADS = 2
1414
else:
15-
NUMITEMS = 100000
15+
NUMITEMS = 5000
1616
NUMTHREADS = 5
1717
NUMMUTATORS = 2
1818

Lib/test/test_genericalias.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,14 @@
5555
from unittest.case import _AssertRaisesContext
5656
from queue import Queue, SimpleQueue
5757
from weakref import WeakSet, ReferenceType, ref
58-
import typing
59-
from typing import Unpack
6058
try:
6159
from tkinter import Event
6260
except ImportError:
6361
Event = None
6462
from string.templatelib import Template, Interpolation
6563

66-
from typing import TypeVar
64+
import typing
65+
from typing import TypeVar, Unpack
6766
T = TypeVar('T')
6867
K = TypeVar('K')
6968
V = TypeVar('V')
@@ -619,6 +618,14 @@ def test_nested_paramspec_specialization(self):
619618
self.assertEqual(deeply_nested_specialized.__args__, ([str, [float], int], float))
620619
self.assertEqual(deeply_nested_specialized.__parameters__, ())
621620

621+
def test_gh150146(self):
622+
# It used to crash:
623+
for container in [memoryview, list, tuple]:
624+
with self.subTest(container=container):
625+
x = container[TypeVar("")]
626+
with self.assertRaises(TypeError):
627+
x[*typing.Mapping[..., ...]]
628+
622629

623630
class TypeIterationTests(unittest.TestCase):
624631
_UNITERABLE_TYPES = (list, tuple)

Lib/test/test_json/test_speedups.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from test.test_json import CTest
2+
from test.support import gc_collect
23

34

45
class BadBool:
@@ -111,3 +112,63 @@ def test_current_indent_level(self):
111112
self.assertEqual(enc(['spam', {'ham': 'eggs'}], 3)[0], expected2)
112113
self.assertRaises(TypeError, enc, ['spam', {'ham': 'eggs'}], 3.0)
113114
self.assertRaises(TypeError, enc, ['spam', {'ham': 'eggs'}])
115+
116+
def test_mutate_dict_items_during_encode(self):
117+
# gh-142831: Clearing the items list via a re-entrant key encoder
118+
# must not cause a use-after-free. BadDict.items() returns a
119+
# mutable list; encode_str clears it while iterating.
120+
items = None
121+
122+
class BadDict(dict):
123+
def items(self):
124+
nonlocal items
125+
items = [("boom", object())]
126+
return items
127+
128+
cleared = False
129+
def encode_str(obj):
130+
nonlocal items, cleared
131+
if items is not None:
132+
items.clear()
133+
items = None
134+
cleared = True
135+
gc_collect()
136+
return '"x"'
137+
138+
encoder = self.json.encoder.c_make_encoder(
139+
None, lambda o: "null",
140+
encode_str, None,
141+
": ", ", ", False,
142+
False, True
143+
)
144+
145+
# Must not crash (use-after-free under ASan before fix)
146+
encoder(BadDict(real=1), 0)
147+
self.assertTrue(cleared)
148+
149+
def test_mutate_list_during_encode(self):
150+
# gh-142831: Clearing a list mid-iteration via the default
151+
# callback must not cause a use-after-free.
152+
call_count = 0
153+
lst = [object() for _ in range(10)]
154+
155+
def default(obj):
156+
nonlocal call_count
157+
call_count += 1
158+
if call_count == 3:
159+
lst.clear()
160+
gc_collect()
161+
return None
162+
163+
encoder = self.json.encoder.c_make_encoder(
164+
None, default,
165+
self.json.encoder.c_encode_basestring, None,
166+
": ", ", ", False,
167+
False, True
168+
)
169+
170+
# Must not crash (use-after-free under ASan before fix)
171+
encoder(lst, 0)
172+
# Verify the mutation path was actually hit and the loop
173+
# stopped iterating after the list was cleared.
174+
self.assertEqual(call_count, 3)

Lib/test/test_ssl.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,59 @@ def dummycallback(sock, servername, ctx, cycle=ctx):
15331533
gc.collect()
15341534
self.assertIs(wr(), None)
15351535

1536+
@unittest.skipUnless(support.Py_GIL_DISABLED,
1537+
"test is only useful if the GIL is disabled")
1538+
@threading_helper.requires_working_threading()
1539+
def test_sni_callback_race(self):
1540+
# Replacing sni_callback while handshakes are in-flight must not
1541+
# crash (use-after-free on the callback in free-threaded builds).
1542+
client_ctx, server_ctx, hostname = testing_context()
1543+
1544+
server_ctx.sni_callback = lambda *a: None
1545+
done = threading.Event()
1546+
1547+
def do_handshakes():
1548+
while not done.is_set():
1549+
c_in = ssl.MemoryBIO()
1550+
c_out = ssl.MemoryBIO()
1551+
s_in = ssl.MemoryBIO()
1552+
s_out = ssl.MemoryBIO()
1553+
client = client_ctx.wrap_bio(
1554+
c_in, c_out, server_hostname=hostname)
1555+
server = server_ctx.wrap_bio(s_in, s_out, server_side=True)
1556+
for _ in range(50):
1557+
try:
1558+
client.do_handshake()
1559+
except ssl.SSLWantReadError:
1560+
pass
1561+
except ssl.SSLError:
1562+
break
1563+
if c_out.pending:
1564+
s_in.write(c_out.read())
1565+
try:
1566+
server.do_handshake()
1567+
except ssl.SSLWantReadError:
1568+
pass
1569+
except ssl.SSLError:
1570+
break
1571+
if s_out.pending:
1572+
c_in.write(s_out.read())
1573+
1574+
def toggle_callback():
1575+
while not done.is_set():
1576+
server_ctx.sni_callback = lambda *a: None
1577+
server_ctx.sni_callback = None
1578+
1579+
workers = max(4, (os.cpu_count() or 4) * 2)
1580+
threads = [threading.Thread(target=do_handshakes)
1581+
for _ in range(workers)]
1582+
threads.append(threading.Thread(target=toggle_callback))
1583+
1584+
with threading_helper.catch_threading_exception() as cm:
1585+
with threading_helper.start_threads(threads):
1586+
done.set()
1587+
self.assertIsNone(cm.exc_value)
1588+
15361589
def test_cert_store_stats(self):
15371590
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
15381591
self.assertEqual(ctx.cert_store_stats(),

Lib/test/test_zipfile/test_core.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,11 +1886,8 @@ def test_write_with_source_date_epoch(self):
18861886

18871887
with zipfile.ZipFile(TESTFN, "r") as zf:
18881888
zip_info = zf.getinfo("test_source_date_epoch.txt")
1889-
get_time = time.localtime(int(os.environ['SOURCE_DATE_EPOCH']))[:6]
1890-
# Compare each element of the date_time tuple
1891-
# Allow for a 1-second difference
1892-
for z_time, g_time in zip(zip_info.date_time, get_time):
1893-
self.assertAlmostEqual(z_time, g_time, delta=1)
1889+
expected_utc = (2025, 1, 1, 7, 19, 58)
1890+
self.assertEqual(zip_info.date_time, expected_utc)
18941891

18951892
def test_write_without_source_date_epoch(self):
18961893
with os_helper.EnvironmentVarGuard() as env:
@@ -1901,9 +1898,13 @@ def test_write_without_source_date_epoch(self):
19011898

19021899
with zipfile.ZipFile(TESTFN, "r") as zf:
19031900
zip_info = zf.getinfo("test_no_source_date_epoch.txt")
1904-
current_time = time.localtime()[:6]
1905-
for z_time, c_time in zip(zip_info.date_time, current_time):
1906-
self.assertAlmostEqual(z_time, c_time, delta=2)
1901+
self.assertTimestampAlmostEqual(time.localtime(), zip_info.date_time, tolerance=2)
1902+
1903+
def assertTimestampAlmostEqual(self, time1, time2, tolerance):
1904+
import datetime
1905+
dt1 = datetime.datetime(*time1[:6])
1906+
dt2 = datetime.datetime(*time2[:6])
1907+
self.assertLessEqual((dt1 - dt2).total_seconds(), tolerance)
19071908

19081909
def test_close(self):
19091910
"""Check that the zipfile is closed after the 'with' block."""

Lib/zipfile/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -663,9 +663,12 @@ def _for_archive(self, archive):
663663
Return self.
664664
"""
665665
# gh-91279: Set the SOURCE_DATE_EPOCH to a specific timestamp
666-
epoch = os.environ.get('SOURCE_DATE_EPOCH')
667-
get_time = int(epoch) if epoch else time.time()
668-
self.date_time = time.localtime(get_time)[:6]
666+
source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH')
667+
668+
if source_date_epoch:
669+
self.date_time = time.gmtime(int(source_date_epoch))[:6]
670+
else:
671+
self.date_time = time.localtime(time.time())[:6]
669672

670673
self.compress_type = archive.compression
671674
self.compress_level = archive.compresslevel
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash when faulthandler is imported more than once.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix a crash on a complex type variable substitution.
2+
3+
``from typing import TypeVar; memoryview[TypeVar("")][*typing.Mapping[...,
4+
...]]`` used to fail due to missing ``NULL`` check on ``_unpack_args`` C
5+
function call.

0 commit comments

Comments
 (0)