Skip to content

Commit b34e838

Browse files
generatedunixname1734921407115435meta-codesync[bot]
authored andcommitted
Import CPython 3.14.7+ stable branch (2026-08-31)
Summary: Imported python/cpython `3.14.7+` from upstream rev [`4e8bce4`](https://www.github.com/python/cpython/commit/4e8bce4d547eab49410ae9fb0fcf7b67c2349056) (committed 2026-08-31 22:19:05+00:00). # Commit Info - Base: (`3.14.7+`) - [`686b543`](https://www.github.com/python/cpython/commit/686b543e1ea13f0161dc46da59770be283c3b54c) (commit date: 2026-08-30 22:10:27+00:00) - Imported: (`3.14.7+`) - [`4e8bce4`](https://www.github.com/python/cpython/commit/4e8bce4d547eab49410ae9fb0fcf7b67c2349056) (commit date: 2026-08-31 22:19:05+00:00) # Noteworthy file changes - Low-signal files (6 added, 1 removed) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GMWRlRbVc_zTkaIFADlTbQBwU7swbr0LAAAz Reviewed By: itamaro Differential Revision: D118270998 fbshipit-source-id: cb1a19102846a23d59835babd7e78f08666d48e6
1 parent 118efe2 commit b34e838

25 files changed

Lines changed: 414 additions & 119 deletions

Doc/library/ast.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2257,7 +2257,7 @@ and classes for traversing abstract syntax trees:
22572257

22582258
In addition, if ``mode`` is ``'func_type'``, the input syntax is
22592259
modified to correspond to :pep:`484` "signature type comments",
2260-
e.g. ``(str, int) -> List[str]``.
2260+
for example ``(str, int) -> List[str]``.
22612261

22622262
Setting ``feature_version`` to a tuple ``(major, minor)`` will result in
22632263
a "best-effort" attempt to parse using that Python version's grammar.

Lib/asyncio/tasks.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,11 @@ def __init__(self, aws, timeout):
562562
self._timeout_handle = None
563563

564564
loop = events.get_event_loop()
565+
self._cur_task = current_task()
565566
todo = {ensure_future(aw, loop=loop) for aw in set(aws)}
566567
for f in todo:
567568
f.add_done_callback(self._handle_completion)
569+
futures.future_add_to_awaited_by(f, self._cur_task)
568570
if todo and timeout is not None:
569571
self._timeout_handle = (
570572
loop.call_later(timeout, self._handle_timeout)
@@ -595,13 +597,15 @@ def __next__(self):
595597
def _handle_timeout(self):
596598
for f in self._todo:
597599
f.remove_done_callback(self._handle_completion)
600+
futures.future_discard_from_awaited_by(f, self._cur_task)
598601
self._done.put_nowait(None) # Sentinel for _wait_for_one().
599602
self._todo.clear() # Can't do todo.remove(f) in the loop.
600603

601604
def _handle_completion(self, f):
602605
if not self._todo:
603606
return # _handle_timeout() was here first.
604607
self._todo.remove(f)
608+
futures.future_discard_from_awaited_by(f, self._cur_task)
605609
self._done.put_nowait(f)
606610
if not self._todo and self._timeout_handle is not None:
607611
self._timeout_handle.cancel()

Lib/configparser.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,8 @@ class RawConfigParser(MutableMapping):
618618
_OPT_TMPL = r"""
619619
(?P<option> # very permissive!
620620
(?:(?!{delim})\S)* # non-delimiter non-whitespace
621-
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
621+
(?:(?:(?!{delim})\s)+ # optionally more
622+
(?:(?!{delim})\S)+)*) # space-separated words
622623
\s*(?P<vi>{delim})\s* # any number of space/tab,
623624
# followed by any of the
624625
# allowed delimiters,
@@ -628,7 +629,8 @@ class RawConfigParser(MutableMapping):
628629
_OPT_NV_TMPL = r"""
629630
(?P<option> # very permissive!
630631
(?:(?!{delim})\S)* # non-delimiter non-whitespace
631-
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
632+
(?:(?:(?!{delim})\s)+ # optionally more
633+
(?:(?!{delim})\S)+)*) # space-separated words
632634
\s*(?: # any number of space/tab,
633635
(?P<vi>{delim})\s* # optionally followed by
634636
# any of the allowed

Lib/test/support/os_helper.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,10 @@ def _rmtree_inner(path):
433433
file=sys.__stderr__)
434434
mode = 0
435435
if stat.S_ISDIR(mode):
436-
_waitfor(_rmtree_inner, fullname, waitall=True)
436+
# Do not follow junctions, which os.lstat() reports
437+
# as directories.
438+
if not os.path.isjunction(fullname):
439+
_waitfor(_rmtree_inner, fullname, waitall=True)
437440
_force_run(fullname, os.rmdir, fullname)
438441
else:
439442
_force_run(fullname, os.unlink, fullname)

Lib/test/test_ast/test_ast.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ def test_parse_invalid_ast(self):
152152
self.assertRaises(TypeError, ast.parse, ast.Constant(42),
153153
optimize=optval)
154154

155+
def test_parse_ast_func_type(self):
156+
# see gh-156689
157+
tree = ast.parse('(int, str) -> bool', mode='func_type')
158+
self.assertEqual(ast.dump(ast.parse(tree, mode='func_type')),
159+
ast.dump(tree))
160+
self.assertRaises(TypeError, ast.parse, ast.Constant(42),
161+
mode='func_type')
162+
self.assertRaises(TypeError, ast.parse, tree, mode='exec')
163+
155164
def test_optimization_levels__debug__(self):
156165
cases = [(-1, '__debug__'), (0, '__debug__'), (1, False), (2, False)]
157166
for (optval, expected) in cases:

Lib/test/test_asyncio/test_graph.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,68 @@ async def main(t1, t2):
271271
]
272272
])
273273

274+
async def test_stack_as_completed(self):
275+
# gh-156523: as_completed() must record the awaiting task
276+
stack_for_inner = None
277+
278+
async def inner():
279+
await asyncio.sleep(0)
280+
nonlocal stack_for_inner
281+
stack_for_inner = capture_test_stack()
282+
283+
async def main(t):
284+
for f in asyncio.as_completed([t]):
285+
await f
286+
287+
t = asyncio.create_task(inner(), name='inner')
288+
await main(t)
289+
self.assertFalse(t._asyncio_awaited_by)
290+
291+
self.assertEqual(stack_for_inner[0], [
292+
'T<inner>',
293+
['s capture_test_stack', 'a inner'],
294+
[
295+
['T<anon>',
296+
['a get', 'a _wait_for_one', 'a main',
297+
'a test_stack_as_completed'],
298+
[]
299+
]
300+
]
301+
])
302+
303+
async def test_stack_as_completed_timeout(self):
304+
# gh-156523: the awaiting task must be dropped when as_completed() times out
305+
stack_for_inner = None
306+
307+
async def inner():
308+
nonlocal stack_for_inner
309+
stack_for_inner = capture_test_stack()
310+
await asyncio.sleep(3600)
311+
312+
async def main(t):
313+
with self.assertRaises(TimeoutError):
314+
for f in asyncio.as_completed([t], timeout=0.01):
315+
await f
316+
317+
t = asyncio.create_task(inner(), name='inner')
318+
await main(t)
319+
self.assertFalse(t._asyncio_awaited_by)
320+
t.cancel()
321+
with self.assertRaises(asyncio.CancelledError):
322+
await t
323+
324+
self.assertEqual(stack_for_inner[0], [
325+
'T<inner>',
326+
['s capture_test_stack', 'a inner'],
327+
[
328+
['T<anon>',
329+
['a get', 'a _wait_for_one', 'a main',
330+
'a test_stack_as_completed_timeout'],
331+
[]
332+
]
333+
]
334+
])
335+
274336
async def test_stack_task(self):
275337

276338
stack_for_inner = None

Lib/test/test_configparser.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class CfgParserTestCaseClass:
4343
default_section = configparser.DEFAULTSECT
4444
interpolation = configparser._UNSET
4545

46-
def newconfig(self, defaults=None):
46+
def newconfig(self, defaults=None, **kwargs):
4747
arguments = dict(
4848
defaults=defaults,
4949
allow_no_value=self.allow_no_value,
@@ -56,6 +56,7 @@ def newconfig(self, defaults=None):
5656
default_section=self.default_section,
5757
interpolation=self.interpolation,
5858
)
59+
arguments.update(kwargs)
5960
instance = self.config_class(**arguments)
6061
return instance
6162

@@ -358,6 +359,32 @@ def test_basic(self):
358359
the larch {0[1]} 1
359360
""".format(self.delimiters)))
360361

362+
@support.subTests('data', [
363+
'foo bar=baz',
364+
'foo bar=baz',
365+
'foo=bar=baz',
366+
'foo = bar=baz',
367+
'foo\t \t=\t \tbar=baz',
368+
])
369+
def test_space_delimiter(self, data):
370+
# gh-156353: Space should be accepted as a delimiter
371+
cf = self.newconfig(delimiters=(' ', '='))
372+
cf.read_string(f"[all]\n{data}")
373+
self.assertEqual(cf.options('all'), ['foo'])
374+
self.assertEqual(cf.get('all', 'foo'), 'bar=baz')
375+
376+
@support.subTests('delimiter', ' =:;#x\t\0\N{RS}\N{CEDILLA}\N{CAT}')
377+
@support.subTests('space_before', ['', ' ', '\t', ' \t'])
378+
@support.subTests('space_after', ['', ' ', '\t', ' \t'])
379+
def test_any_delimiter(self, delimiter, space_before, space_after):
380+
cf = self.newconfig(
381+
delimiters=(delimiter,),
382+
inline_comment_prefixes=None,
383+
)
384+
cf.read_string(f"[all]\nfoo{space_before}{delimiter}{space_after}bar=baz")
385+
self.assertEqual(cf.options('all'), ['foo'])
386+
self.assertEqual(cf.get('all', 'foo'), 'bar=baz')
387+
361388
def test_basic_from_dict(self):
362389
config = {
363390
"Foo Bar": {
@@ -1991,8 +2018,8 @@ class ConvertersTestCase(BasicTestCase, unittest.TestCase):
19912018

19922019
config_class = configparser.ConfigParser
19932020

1994-
def newconfig(self, defaults=None):
1995-
instance = super().newconfig(defaults=defaults)
2021+
def newconfig(self, defaults=None, **kwargs):
2022+
instance = super().newconfig(defaults=defaults, **kwargs)
19962023
instance.converters['list'] = lambda v: [e.strip() for e in v.split()
19972024
if e.strip()]
19982025
return instance

Lib/test/test_xml_etree.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,34 @@ def bxml(encoding, body=''):
10241024
self.assertRaises(ValueError, ET.XML, xml('undefined').encode('ascii'))
10251025
self.assertRaises(LookupError, ET.XML, xml('xxx').encode('ascii'))
10261026

1027+
def test_parse_text_source(self):
1028+
# gh-99064: The encoding declared in the document does not apply
1029+
# to a source which is already decoded.
1030+
def check(encoding, body):
1031+
xml = (f"<?xml version='1.0' encoding='{encoding}'?>"
1032+
f"<xml>{body}</xml>")
1033+
with self.subTest(encoding=encoding):
1034+
self.assertEqual(ET.parse(io.StringIO(xml)).getroot().text,
1035+
body)
1036+
# the same with an explicitly created parser
1037+
self.assertEqual(
1038+
ET.parse(io.StringIO(xml), ET.XMLParser()).getroot().text,
1039+
body)
1040+
check("ascii", 'a')
1041+
check("iso-8859-1", '\xbd')
1042+
check("iso-8859-15", '\u20ac')
1043+
check("cp437", '\u221a')
1044+
check("utf-8", '\u4e2d')
1045+
# not ASCII compatible, unsupported for a bytes source
1046+
check("utf-16", '\u4e2d')
1047+
check("utf-32", '\u4e2d')
1048+
1049+
def test_parse_text_source_multiple_chunks(self):
1050+
# the encoding is overridden before the first chunk is parsed
1051+
body = '\xe4' * 100_000
1052+
xml = "<?xml version='1.0' encoding='ISO-8859-1'?><xml>%s</xml>" % body
1053+
self.assertEqual(ET.parse(io.StringIO(xml)).getroot().text, body)
1054+
10271055
def test_methods(self):
10281056
# Test serialization methods.
10291057

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Don't set the result in error in :c:func:`PyLong_AsInt32`,
2+
:c:func:`PyLong_AsUInt32`, :c:func:`PyLong_AsInt64` and
3+
:c:func:`PyLong_AsUInt64`. Leave the result unchanged in this case. Patch by
4+
Victor Stinner.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix an out-of-bounds read in :func:`compile` and :func:`ast.parse` when an AST
2+
object is passed with ``mode='func_type'``.

0 commit comments

Comments
 (0)