Skip to content

Commit 585e0d6

Browse files
itamarometa-codesync[bot]
authored andcommitted
Import CPython 3.14.6+ stable branch (2026-07-25)
Summary: Imported python/cpython `3.14.6+` from upstream rev [`e76d333`](https://www.github.com/python/cpython/commit/e76d3333cbfa82e32aa4f8cf0b52d2664e90eb44) (committed 2026-07-25 03:56:33+00:00). # Commit Info - Base: (`3.14.6+`) - [`37a5c69`](https://www.github.com/python/cpython/commit/37a5c690d033099276c8a264c1a660819863bd68) (commit date: 2026-07-23 08:21:52+00:00) - Imported: (`3.14.6+`) - [`e76d333`](https://www.github.com/python/cpython/commit/e76d3333cbfa82e32aa4f8cf0b52d2664e90eb44) (commit date: 2026-07-25 03:56:33+00:00) # Noteworthy file changes - Native files (1 added): ``` + Modules/_testlimitedcapi/capsule.c ``` - Test files (1 added) - Low-signal files (10 added) (NEWS.d, docs, .github) Complete list of added/removed files: https://www.internalfb.com/intern/everpaste/?color=0&handle=GLTSliGeK1K9mLoDAABDYUCHmlkObr0LAAAz Reviewed By: yoney Differential Revision: D113652391 fbshipit-source-id: d976418ac47696613ec8b0e283f50d65d8e5c85f
1 parent 75ed362 commit 585e0d6

37 files changed

Lines changed: 504 additions & 70 deletions

.github/workflows/build.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,14 @@ jobs:
187187
- name: Check for unsupported C global variables
188188
if: github.event_name == 'pull_request' # $GITHUB_EVENT_NAME
189189
run: make check-c-globals
190-
- name: Check for undocumented C APIs
191-
run: make check-c-api-docs
192190

191+
check-c-api-docs:
192+
name: C API Docs
193+
needs: build-context
194+
if: >-
195+
needs.build-context.outputs.run-tests == 'true'
196+
|| needs.build-context.outputs.run-docs == 'true'
197+
uses: ./.github/workflows/reusable-check-c-api-docs.yml
193198

194199
build-windows:
195200
name: >-
@@ -674,6 +679,7 @@ jobs:
674679
- check-docs
675680
- check-autoconf-regen
676681
- check-generated-files
682+
- check-c-api-docs
677683
- build-windows
678684
- build-windows-msi
679685
- build-macos
@@ -710,6 +716,12 @@ jobs:
710716
'
711717
|| ''
712718
}}
719+
${{
720+
!fromJSON(needs.build-context.outputs.run-tests)
721+
&& !fromJSON(needs.build-context.outputs.run-docs)
722+
&& 'check-c-api-docs,'
723+
|| ''
724+
}}
713725
${{ !fromJSON(needs.build-context.outputs.run-windows-tests) && 'build-windows,' || '' }}
714726
${{
715727
!fromJSON(needs.build-context.outputs.run-ci-fuzz)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Reusable C API Docs Check
2+
3+
on:
4+
workflow_call:
5+
6+
permissions:
7+
contents: read
8+
9+
env:
10+
FORCE_COLOR: 1
11+
12+
jobs:
13+
check-c-api-docs:
14+
name: 'Check if all C APIs are documented'
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 5
17+
steps:
18+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
19+
with:
20+
persist-credentials: false
21+
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
22+
with:
23+
python-version: '3.x'
24+
- name: Check for undocumented C APIs
25+
run: python Tools/check-c-api-docs/main.py

Doc/library/difflib.rst

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,18 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module.
4444
complicated way on how many elements the sequences have in common; best case
4545
time is linear.
4646

47-
**Automatic junk heuristic:** :class:`SequenceMatcher` supports a heuristic that
48-
automatically treats certain sequence items as junk. The heuristic counts how many
49-
times each individual item appears in the sequence. If an item's duplicates (after
50-
the first one) account for more than 1% of the sequence and the sequence is at least
51-
200 items long, this item is marked as "popular" and is treated as junk for
52-
the purpose of sequence matching. This heuristic can be turned off by setting
53-
the ``autojunk`` argument to ``False`` when creating the :class:`SequenceMatcher`.
47+
**Junk**: :class:`SequenceMatcher` accepts an ``isjunk`` predicate and an
48+
``autojunk`` flag. Items that are considered as junk will not be considered
49+
to find similar content blocks. This can produce better results for humans
50+
(typically breaking on whitespace) and faster (because it reduces the number
51+
of possible combinations). But it can also cause pathological cases where
52+
too many items considered junk cause an unexpectedly large (but correct)
53+
diff result.
54+
You should consider tuning them or turning them off depending on your data.
55+
Moreover, only the second sequence is inspected for junk. This causes the diff
56+
output to not be symmetrical.
57+
When ``autojunk=True``, it will consider as junk the items that account for more
58+
than 1% of the sequence, if it is at least 200 items long.
5459

5560
.. versionchanged:: 3.2
5661
Added the *autojunk* parameter.
@@ -554,16 +559,6 @@ The :class:`SequenceMatcher` class has this constructor:
554559
to try :meth:`quick_ratio` or :meth:`real_quick_ratio` first to get an
555560
upper bound.
556561

557-
.. note::
558-
559-
Caution: The result of a :meth:`ratio` call may depend on the order of
560-
the arguments. For instance::
561-
562-
>>> SequenceMatcher(None, 'tide', 'diet').ratio()
563-
0.25
564-
>>> SequenceMatcher(None, 'diet', 'tide').ratio()
565-
0.5
566-
567562

568563
.. method:: quick_ratio()
569564

Lib/asyncio/base_subprocess.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def __init__(self, loop, protocol, args, shell,
2222
self._proc = None
2323
self._pid = None
2424
self._returncode = None
25-
self._exit_waiters = []
25+
self._exit_waiters = set()
2626
self._pending_calls = collections.deque()
2727
self._pipes = {}
2828
self._finished = False
@@ -211,6 +211,14 @@ async def _connect_pipes(self, waiter):
211211
except (SystemExit, KeyboardInterrupt):
212212
raise
213213
except BaseException as exc:
214+
# Close any pipes that were already connected before the
215+
# error/cancellation to avoid leaking file descriptors.
216+
for proto in self._pipes.values():
217+
if proto is not None:
218+
proto.pipe.close()
219+
for raw_pipe in (proc.stdin, proc.stdout, proc.stderr):
220+
if raw_pipe is not None:
221+
raw_pipe.close()
214222
if waiter is not None and not waiter.cancelled():
215223
waiter.set_exception(exc)
216224
else:
@@ -260,8 +268,12 @@ async def _wait(self):
260268
return self._returncode
261269

262270
waiter = self._loop.create_future()
263-
self._exit_waiters.append(waiter)
264-
return await waiter
271+
self._exit_waiters.add(waiter)
272+
try:
273+
return await waiter
274+
finally:
275+
if self._exit_waiters is not None:
276+
self._exit_waiters.discard(waiter)
265277

266278
def _try_finish(self):
267279
assert not self._finished

Lib/asyncio/selector_events.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -582,14 +582,10 @@ def _sock_sendall(self, fut, sock, view, pos):
582582
pos[0] = start
583583

584584
async def sock_sendto(self, sock, data, address):
585-
"""Send data to the socket.
585+
"""Send a datagram from sock to address.
586586
587-
The socket must be connected to a remote socket. This method
588-
continues to send data from data until either all data has been
589-
sent or an error occurs. None is returned on success. On error,
590-
an exception is raised, and there is no way to determine how much
591-
data, if any, was successfully processed by the receiving end of
592-
the connection.
587+
The socket does not have to be connected. This method sends the
588+
whole datagram in a single call. Return the number of bytes sent.
593589
"""
594590
base_events._check_ssl_socket(sock)
595591
if self._debug and sock.gettimeout() != 0:

Lib/asyncio/unix_events.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ async def _make_subprocess_transport(self, protocol, args, shell,
212212
raise
213213
except BaseException:
214214
transp.close()
215-
await transp._wait()
215+
await tasks.shield(transp._wait())
216216
raise
217217

218218
return transp
@@ -639,7 +639,8 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
639639
self._conn_lost = 0
640640
self._closing = False # Set when close() or write_eof() called.
641641

642-
mode = os.fstat(self._fileno).st_mode
642+
pipe_stat = os.fstat(self._fileno)
643+
mode = pipe_stat.st_mode
643644
is_char = stat.S_ISCHR(mode)
644645
is_fifo = stat.S_ISFIFO(mode)
645646
is_socket = stat.S_ISSOCK(mode)
@@ -656,7 +657,19 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
656657
# On AIX, the reader trick (to be notified when the read end of the
657658
# socket is closed) only works for sockets. On other platforms it
658659
# works for pipes and sockets. (Exception: OS X 10.4? Issue #19294.)
659-
if is_socket or (is_fifo and not sys.platform.startswith("aix")):
660+
# On macOS, the trick misfires for named FIFOs (but not for pipes
661+
# created with os.pipe(), which have st_nlink == 0): the write end
662+
# polls as readable whenever unread data sits in the FIFO, and no
663+
# event is delivered when the read end is closed, so it can only
664+
# ever report a false disconnection (gh-145030). The same xnu
665+
# behaviour applies on iOS/tvOS/watchOS (sys.platform is not
666+
# "darwin" there).
667+
is_named_fifo_on_apple = (
668+
sys.platform in {"darwin", "ios", "tvos", "watchos"}
669+
and is_fifo and pipe_stat.st_nlink > 0)
670+
if is_socket or (is_fifo
671+
and not sys.platform.startswith("aix")
672+
and not is_named_fifo_on_apple):
660673
# only start reading when connection_made() has been called
661674
self._loop.call_soon(self._loop._add_reader,
662675
self._fileno, self._read_ready)

Lib/asyncio/windows_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ async def _make_subprocess_transport(self, protocol, args, shell,
408408
raise
409409
except BaseException:
410410
transp.close()
411-
await transp._wait()
411+
await tasks.shield(transp._wait())
412412
raise
413413

414414
return transp

Lib/ctypes/util.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,7 @@ def _findLib_ld(name):
421421
result = None
422422
try:
423423
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
424-
stderr=subprocess.PIPE,
425-
universal_newlines=True)
424+
stderr=subprocess.PIPE)
426425
out, _ = p.communicate()
427426
res = re.findall(expr, os.fsdecode(out))
428427
for file in res:

Lib/logging/config.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import os
3333
import queue
3434
import re
35+
import socket
3536
import struct
3637
import threading
3738
import traceback
@@ -1022,6 +1023,15 @@ class ConfigSocketReceiver(ThreadingTCPServer):
10221023

10231024
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
10241025
handler=None, ready=None, verify=None):
1026+
# The host can have no IPv4 address, for example if "localhost"
1027+
# is only aliased to ::1. Leave resolution errors to the server.
1028+
try:
1029+
infos = socket.getaddrinfo(host, port, type=socket.SOCK_STREAM)
1030+
except OSError:
1031+
pass
1032+
else:
1033+
if not any(info[0] == socket.AF_INET for info in infos):
1034+
self.address_family = infos[0][0]
10251035
ThreadingTCPServer.__init__(self, (host, port), handler)
10261036
with logging._lock:
10271037
self.abort = 0
@@ -1053,9 +1063,14 @@ def __init__(self, rcvr, hdlr, port, verify):
10531063
self.ready = threading.Event()
10541064

10551065
def run(self):
1056-
server = self.rcvr(port=self.port, handler=self.hdlr,
1057-
ready=self.ready,
1058-
verify=self.verify)
1066+
try:
1067+
server = self.rcvr(port=self.port, handler=self.hdlr,
1068+
ready=self.ready,
1069+
verify=self.verify)
1070+
except BaseException:
1071+
# Do not leave the caller waiting for ready forever.
1072+
self.ready.set()
1073+
raise
10591074
if self.port == 0:
10601075
self.port = server.server_address[1]
10611076
self.ready.set()

Lib/test/test_asyncio/test_events.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,48 @@ def reader(data):
17251725
self.loop.run_until_complete(proto.done)
17261726
self.assertEqual('CLOSED', proto.state)
17271727

1728+
@unittest.skipUnless(sys.platform != 'win32',
1729+
"Don't support pipes for Windows")
1730+
@unittest.skipUnless(hasattr(os, 'mkfifo'), 'requires os.mkfifo()')
1731+
def test_write_named_fifo_unread_data(self):
1732+
# gh-145030: on macOS, the write end of a named FIFO polls as
1733+
# readable while unread data sits in the FIFO, which made the
1734+
# transport misinterpret the event as the reader hanging up
1735+
# and close itself.
1736+
path = os_helper.TESTFN
1737+
os.mkfifo(path)
1738+
self.assertNotEqual(os.stat(path).st_nlink, 0)
1739+
self.addCleanup(os_helper.unlink, path)
1740+
rfd = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
1741+
self.addCleanup(os.close, rfd)
1742+
wfd = os.open(path, os.O_WRONLY | os.O_NONBLOCK)
1743+
pipeobj = io.open(wfd, 'wb', 1024)
1744+
1745+
proto = MyWritePipeProto(loop=self.loop)
1746+
connect = self.loop.connect_write_pipe(lambda: proto, pipeobj)
1747+
transport, p = self.loop.run_until_complete(connect)
1748+
self.assertIs(p, proto)
1749+
self.assertEqual('CONNECTED', proto.state)
1750+
1751+
transport.write(b'1')
1752+
# Iterate the event loop while the data stays unread in the FIFO;
1753+
# the transport must not detect a false disconnection.
1754+
for _ in range(10):
1755+
test_utils.run_briefly(self.loop)
1756+
self.assertEqual('CONNECTED', proto.state)
1757+
self.assertFalse(transport.is_closing())
1758+
self.assertEqual(b'1', os.read(rfd, 1024))
1759+
1760+
transport.write(b'2345')
1761+
for _ in range(10):
1762+
test_utils.run_briefly(self.loop)
1763+
self.assertEqual('CONNECTED', proto.state)
1764+
self.assertEqual(b'2345', os.read(rfd, 1024))
1765+
1766+
transport.close()
1767+
self.loop.run_until_complete(proto.done)
1768+
self.assertEqual('CLOSED', proto.state)
1769+
17281770
@unittest.skipUnless(sys.platform != 'win32',
17291771
"Don't support pipes for Windows")
17301772
def test_write_pipe_disconnect_on_close(self):

0 commit comments

Comments
 (0)