Skip to content

Commit 148d4c9

Browse files
Merge remote-tracking branch 'upstream/main' into capsule-import-submodules
2 parents 833f59f + d24d9d0 commit 148d4c9

41 files changed

Lines changed: 778 additions & 106 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Doc/library/exceptions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ The following exceptions are the exceptions that are usually raised.
215215

216216
The object that was accessed for the named attribute.
217217

218+
When possible, :attr:`name` and :attr:`obj` are set automatically.
219+
218220
.. versionchanged:: 3.10
219221
Added the :attr:`name` and :attr:`obj` attributes.
220222

Doc/library/stdtypes.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4876,8 +4876,9 @@ copying.
48764876
Cast a memoryview to a new format or shape. *shape* defaults to
48774877
``[byte_length//new_itemsize]``, which means that the result view
48784878
will be one-dimensional. The return value is a new memoryview, but
4879-
the buffer itself is not copied. Supported casts are 1D -> C-:term:`contiguous`
4880-
and C-contiguous -> 1D.
4879+
the buffer itself is not copied. Supported casts are
4880+
1D -> C-:term:`contiguous`, C-contiguous -> 1D, and
4881+
F-contiguous -> 1D.
48814882

48824883
The destination format is restricted to a single element native format in
48834884
:mod:`struct` syntax. One of the formats must be a byte format
@@ -4964,6 +4965,10 @@ copying.
49644965
.. versionchanged:: 3.5
49654966
The source format is no longer restricted when casting to a byte view.
49664967

4968+
.. versionchanged:: next
4969+
Casting a multi-dimensional F-contiguous view to a one-dimensional
4970+
view is now supported.
4971+
49674972
.. method:: count(value, /)
49684973

49694974
Count the number of occurrences of *value*.

Doc/whatsnew/3.16.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ New features
7575
Other language changes
7676
======================
7777

78+
* :meth:`memoryview.cast` now allows casting a multidimensional
79+
F-contiguous view to a one-dimensional view.
80+
(Contributed by Jaemin Park in :gh:`91484`.)
81+
7882
* :ref:`Frame objects <frame-objects>` now support :mod:`weak references
7983
<weakref>`. This allows associating extra data with active frames,
8084
for example in debuggers, without keeping the frames (and everything

Lib/asyncio/__main__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,14 @@ def interrupt(self) -> None:
212212
loop = asyncio.new_event_loop()
213213
asyncio.set_event_loop(loop)
214214

215-
repl_locals = {'asyncio': asyncio}
216-
for key in {'__name__', '__package__',
217-
'__loader__', '__spec__',
218-
'__builtins__', '__file__'}:
219-
repl_locals[key] = locals()[key]
215+
repl_locals = {
216+
'asyncio': asyncio,
217+
'__name__': __name__,
218+
'__package__': None,
219+
'__loader__': __loader__,
220+
'__spec__': None,
221+
'__builtins__': __builtins__,
222+
}
220223

221224
console = AsyncIOInteractiveConsole(repl_locals, loop)
222225

Lib/asyncio/staggered.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,7 @@ def task_done(task):
9090
return
9191
unhandled_exceptions.append(exc)
9292

93-
async def run_one_coro(ok_to_start, previous_failed) -> None:
94-
# in eager tasks this waits for the calling task to append this task
95-
# to running_tasks, in regular tasks this wait is a no-op that does
96-
# not yield a future. See gh-124309.
97-
await ok_to_start.wait()
93+
async def run_one_coro(previous_failed) -> None:
9894
# Wait for the previous task to finish, or for delay seconds
9995
if previous_failed is not None:
10096
with contextlib.suppress(exceptions_mod.TimeoutError):
@@ -110,14 +106,13 @@ async def run_one_coro(ok_to_start, previous_failed) -> None:
110106
return
111107
# Start task that will run the next coroutine
112108
this_failed = locks.Event()
113-
next_ok_to_start = locks.Event()
114-
next_task = loop.create_task(run_one_coro(next_ok_to_start, this_failed))
109+
next_task = loop.create_task(
110+
run_one_coro(this_failed),
111+
eager_start=False,
112+
)
115113
futures.future_add_to_awaited_by(next_task, parent_task)
116114
running_tasks.add(next_task)
117115
next_task.add_done_callback(task_done)
118-
# next_task has been appended to running_tasks so next_task is ok to
119-
# start.
120-
next_ok_to_start.set()
121116
# Prepare place to put this coroutine's exceptions if not won
122117
exceptions.append(None)
123118
assert len(exceptions) == this_index + 1
@@ -149,13 +144,11 @@ async def run_one_coro(ok_to_start, previous_failed) -> None:
149144

150145
propagate_cancellation_error = None
151146
try:
152-
ok_to_start = locks.Event()
153-
first_task = loop.create_task(run_one_coro(ok_to_start, None))
147+
first_task = loop.create_task(run_one_coro(None), eager_start=False)
154148
futures.future_add_to_awaited_by(first_task, parent_task)
155149
running_tasks.add(first_task)
156150
first_task.add_done_callback(task_done)
157-
# first_task has been appended to running_tasks so first_task is ok to start.
158-
ok_to_start.set()
151+
# first_task has been appended to running_tasks before the event loop starts running it.
159152
propagate_cancellation_error = None
160153
# Make sure no tasks are left running if we leave this function
161154
while running_tasks:

Lib/asyncio/unix_events.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,8 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
640640
self._conn_lost = 0
641641
self._closing = False # Set when close() or write_eof() called.
642642

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

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
@@ -1004,6 +1005,15 @@ class ConfigSocketReceiver(ThreadingTCPServer):
10041005

10051006
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
10061007
handler=None, ready=None, verify=None):
1008+
# The host can have no IPv4 address, for example if "localhost"
1009+
# is only aliased to ::1. Leave resolution errors to the server.
1010+
try:
1011+
infos = socket.getaddrinfo(host, port, type=socket.SOCK_STREAM)
1012+
except OSError:
1013+
pass
1014+
else:
1015+
if not any(info[0] == socket.AF_INET for info in infos):
1016+
self.address_family = infos[0][0]
10071017
ThreadingTCPServer.__init__(self, (host, port), handler)
10081018
with logging._lock:
10091019
self.abort = 0
@@ -1035,9 +1045,14 @@ def __init__(self, rcvr, hdlr, port, verify):
10351045
self.ready = threading.Event()
10361046

10371047
def run(self):
1038-
server = self.rcvr(port=self.port, handler=self.hdlr,
1039-
ready=self.ready,
1040-
verify=self.verify)
1048+
try:
1049+
server = self.rcvr(port=self.port, handler=self.hdlr,
1050+
ready=self.ready,
1051+
verify=self.verify)
1052+
except BaseException:
1053+
# Do not leave the caller waiting for ready forever.
1054+
self.ready.set()
1055+
raise
10411056
if self.port == 0:
10421057
self.port = server.server_address[1]
10431058
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):

Lib/test/test_asyncio/test_transports.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,29 @@ def get_write_buffer_size(self):
9898
self.assertTrue(transport._protocol_paused)
9999
self.assertEqual(transport.get_write_buffer_limits(), (128, 256))
100100

101+
def test_flowcontrol_mixin_pause_writing_exception(self):
102+
103+
class MyTransport(transports._FlowControlMixin,
104+
transports.Transport):
105+
106+
def get_write_buffer_size(self):
107+
return 2000
108+
109+
loop = mock.Mock()
110+
transport = MyTransport(loop=loop)
111+
protocol = mock.Mock()
112+
protocol.pause_writing.side_effect = RuntimeError("boom")
113+
transport._protocol = protocol
114+
transport.set_write_buffer_limits(high=1000, low=100)
115+
transport._maybe_pause_protocol()
116+
protocol.pause_writing.assert_called_once()
117+
loop.call_exception_handler.assert_called_once()
118+
args = loop.call_exception_handler.call_args[0][0]
119+
120+
self.assertIn("protocol.pause_writing() failed", args["message"])
121+
self.assertIsInstance(args["exception"], RuntimeError)
122+
self.assertTrue(transport._protocol_paused)
123+
101124
def test_flowcontrol_mixin_compute_write_limits(self):
102125

103126
class MyTransport(transports._FlowControlMixin,

Lib/test/test_buffer.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2877,6 +2877,32 @@ class BEPoint:
28772877
self.assertEqual(m2.strides, (1,))
28782878
self.assertEqual(m2.suboffsets, ())
28792879

2880+
def test_memoryview_cast_f_contiguous_ND_1D(self):
2881+
nd = ndarray(list(range(12)), shape=[3, 4], format='B', flags=ND_FORTRAN)
2882+
m = memoryview(nd)
2883+
self.assertTrue(m.f_contiguous)
2884+
self.assertTrue(m.contiguous)
2885+
2886+
m1 = m.cast('B')
2887+
self.assertEqual(m1.ndim, 1)
2888+
self.assertEqual(m1.shape, (m.nbytes,))
2889+
self.assertEqual(m1.strides, (1,))
2890+
self.assertTrue(m1.c_contiguous)
2891+
self.assertTrue(m1.contiguous)
2892+
self.assertEqual(m1.tobytes(), memoryview(nd).tobytes(order='F'))
2893+
2894+
for fmt in ('B', 'b', 'c', 'H', 'I'):
2895+
size = struct.calcsize(fmt)
2896+
if m.nbytes % size == 0:
2897+
m2 = m.cast(fmt)
2898+
self.assertEqual(m2.ndim, 1)
2899+
self.assertEqual(m2.shape, (m.nbytes // size,))
2900+
self.assertTrue(m2.contiguous)
2901+
2902+
m3 = m[::-1]
2903+
with self.assertRaises(TypeError):
2904+
m3.cast('B')
2905+
28802906
def test_memoryview_tolist(self):
28812907

28822908
# Most tolist() tests are in self.verify() etc.

0 commit comments

Comments
 (0)