Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
a096314
gh-150546: Fix cleanup for replacement_locations in _PyCode_New() (#1…
lpyu001 Jul 24, 2026
ae72d0c
gh-153785: Generate `AttributeError` messages from context (#153786)
johnslavik Jul 24, 2026
da46947
gh-154592: Fix test_peg_generator in non-UTF-8 locales with a non-ASC…
serhiy-storchaka Jul 24, 2026
41a087a
gh-146011: Fix use-after-free in `signaldict_repr` after deletion (#1…
brijkapadia Jul 24, 2026
b1e530e
gh-154582: Fix test_invalid_utf8_arg in non-UTF-8 multibyte locales (…
serhiy-storchaka Jul 24, 2026
a400767
gh-91484: Allow memoryview cast for F-contiguous (GH-137803)
jamie2779 Jul 24, 2026
409f324
gh-145030: Fix asyncio write pipe transport for named FIFOs on macOS …
bhuvi27 Jul 24, 2026
ad1cea6
gh-150361: add test for FlowControlMixin pause_writing exception hand…
azibom Jul 24, 2026
fb64db2
gh-140326: disable the relative import in asyncio REPL (#140327)
Locked-chess-official Jul 24, 2026
74f4b06
gh-145107: simplify asyncio staggered race using eager_start=False (#…
graingert Jul 24, 2026
45a10f5
gh-73458: Fix logging.config.listen() on a host without an IPv4 addre…
serhiy-storchaka Jul 24, 2026
d24d9d0
gh-76595: Add tests for PyCapsule_Import() (GH-154588)
serhiy-storchaka Jul 24, 2026
d5c1b29
gh-153419: Fix several issues around bytearray __init__ (#153498)
stestagg Jul 24, 2026
6cb93bd
gh-154580: Fix python-gdb.py pretty-printing non-ASCII strings in non…
serhiy-storchaka Jul 24, 2026
46acb79
gh-70990: Support bytes addresses of Unix sockets in SysLogHandler (G…
serhiy-storchaka Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Doc/library/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ The following exceptions are the exceptions that are usually raised.

The object that was accessed for the named attribute.

When possible, :attr:`name` and :attr:`obj` are set automatically.

.. versionchanged:: 3.10
Added the :attr:`name` and :attr:`obj` attributes.

Expand Down
6 changes: 5 additions & 1 deletion Doc/library/logging.handlers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,8 @@ supports sending logging messages to a remote or local Unix syslog.
the form of a ``(host, port)`` tuple. If *address* is not specified,
``('localhost', 514)`` is used. The address is used to open a socket. An
alternative to providing a ``(host, port)`` tuple is providing an address as a
string, for example '/dev/log'. In this case, a Unix domain socket is used to
string or a :class:`bytes` object, for example '/dev/log'.
In this case, a Unix domain socket is used to
send the message to the syslog. If *facility* is not specified,
:const:`LOG_USER` is used. The type of socket opened depends on the
*socktype* argument, which defaults to :const:`socket.SOCK_DGRAM` and thus
Expand Down Expand Up @@ -664,6 +665,9 @@ supports sending logging messages to a remote or local Unix syslog.
.. versionchanged:: 3.14
*timeout* was added.

.. versionchanged:: next
*address* can now be a :class:`bytes` object.

.. method:: close()

Closes the socket to the remote host.
Expand Down
9 changes: 7 additions & 2 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4876,8 +4876,9 @@ copying.
Cast a memoryview to a new format or shape. *shape* defaults to
``[byte_length//new_itemsize]``, which means that the result view
will be one-dimensional. The return value is a new memoryview, but
the buffer itself is not copied. Supported casts are 1D -> C-:term:`contiguous`
and C-contiguous -> 1D.
the buffer itself is not copied. Supported casts are
1D -> C-:term:`contiguous`, C-contiguous -> 1D, and
F-contiguous -> 1D.

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

.. versionchanged:: next
Casting a multi-dimensional F-contiguous view to a one-dimensional
view is now supported.

.. method:: count(value, /)

Count the number of occurrences of *value*.
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ New features
Other language changes
======================

* :meth:`memoryview.cast` now allows casting a multidimensional
F-contiguous view to a one-dimensional view.
(Contributed by Jaemin Park in :gh:`91484`.)

* :ref:`Frame objects <frame-objects>` now support :mod:`weak references
<weakref>`. This allows associating extra data with active frames,
for example in debuggers, without keeping the frames (and everything
Expand Down
13 changes: 8 additions & 5 deletions Lib/asyncio/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,14 @@ def interrupt(self) -> None:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

repl_locals = {'asyncio': asyncio}
for key in {'__name__', '__package__',
'__loader__', '__spec__',
'__builtins__', '__file__'}:
repl_locals[key] = locals()[key]
repl_locals = {
'asyncio': asyncio,
'__name__': __name__,
'__package__': None,
'__loader__': __loader__,
'__spec__': None,
'__builtins__': __builtins__,
}

console = AsyncIOInteractiveConsole(repl_locals, loop)

Expand Down
21 changes: 7 additions & 14 deletions Lib/asyncio/staggered.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,7 @@ def task_done(task):
return
unhandled_exceptions.append(exc)

async def run_one_coro(ok_to_start, previous_failed) -> None:
# in eager tasks this waits for the calling task to append this task
# to running_tasks, in regular tasks this wait is a no-op that does
# not yield a future. See gh-124309.
await ok_to_start.wait()
async def run_one_coro(previous_failed) -> None:
# Wait for the previous task to finish, or for delay seconds
if previous_failed is not None:
with contextlib.suppress(exceptions_mod.TimeoutError):
Expand All @@ -110,14 +106,13 @@ async def run_one_coro(ok_to_start, previous_failed) -> None:
return
# Start task that will run the next coroutine
this_failed = locks.Event()
next_ok_to_start = locks.Event()
next_task = loop.create_task(run_one_coro(next_ok_to_start, this_failed))
next_task = loop.create_task(
run_one_coro(this_failed),
eager_start=False,
)
futures.future_add_to_awaited_by(next_task, parent_task)
running_tasks.add(next_task)
next_task.add_done_callback(task_done)
# next_task has been appended to running_tasks so next_task is ok to
# start.
next_ok_to_start.set()
# Prepare place to put this coroutine's exceptions if not won
exceptions.append(None)
assert len(exceptions) == this_index + 1
Expand Down Expand Up @@ -149,13 +144,11 @@ async def run_one_coro(ok_to_start, previous_failed) -> None:

propagate_cancellation_error = None
try:
ok_to_start = locks.Event()
first_task = loop.create_task(run_one_coro(ok_to_start, None))
first_task = loop.create_task(run_one_coro(None), eager_start=False)
futures.future_add_to_awaited_by(first_task, parent_task)
running_tasks.add(first_task)
first_task.add_done_callback(task_done)
# first_task has been appended to running_tasks so first_task is ok to start.
ok_to_start.set()
# first_task has been appended to running_tasks before the event loop starts running it.
propagate_cancellation_error = None
# Make sure no tasks are left running if we leave this function
while running_tasks:
Expand Down
17 changes: 15 additions & 2 deletions Lib/asyncio/unix_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,8 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
self._conn_lost = 0
self._closing = False # Set when close() or write_eof() called.

mode = os.fstat(self._fileno).st_mode
pipe_stat = os.fstat(self._fileno)
mode = pipe_stat.st_mode
is_char = stat.S_ISCHR(mode)
is_fifo = stat.S_ISFIFO(mode)
is_socket = stat.S_ISSOCK(mode)
Expand All @@ -657,7 +658,19 @@ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
# On AIX, the reader trick (to be notified when the read end of the
# socket is closed) only works for sockets. On other platforms it
# works for pipes and sockets. (Exception: OS X 10.4? Issue #19294.)
if is_socket or (is_fifo and not sys.platform.startswith("aix")):
# On macOS, the trick misfires for named FIFOs (but not for pipes
# created with os.pipe(), which have st_nlink == 0): the write end
# polls as readable whenever unread data sits in the FIFO, and no
# event is delivered when the read end is closed, so it can only
# ever report a false disconnection (gh-145030). The same xnu
# behaviour applies on iOS/tvOS/watchOS (sys.platform is not
# "darwin" there).
is_named_fifo_on_apple = (
sys.platform in {"darwin", "ios", "tvos", "watchos"}
and is_fifo and pipe_stat.st_nlink > 0)
if is_socket or (is_fifo
and not sys.platform.startswith("aix")
and not is_named_fifo_on_apple):
# only start reading when connection_made() has been called
self._loop.call_soon(self._loop._add_reader,
self._fileno, self._read_ready)
Expand Down
21 changes: 18 additions & 3 deletions Lib/logging/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import os
import queue
import re
import socket
import struct
import threading
import traceback
Expand Down Expand Up @@ -1004,6 +1005,15 @@ class ConfigSocketReceiver(ThreadingTCPServer):

def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
handler=None, ready=None, verify=None):
# The host can have no IPv4 address, for example if "localhost"
# is only aliased to ::1. Leave resolution errors to the server.
try:
infos = socket.getaddrinfo(host, port, type=socket.SOCK_STREAM)
except OSError:
pass
else:
if not any(info[0] == socket.AF_INET for info in infos):
self.address_family = infos[0][0]
ThreadingTCPServer.__init__(self, (host, port), handler)
with logging._lock:
self.abort = 0
Expand Down Expand Up @@ -1035,9 +1045,14 @@ def __init__(self, rcvr, hdlr, port, verify):
self.ready = threading.Event()

def run(self):
server = self.rcvr(port=self.port, handler=self.hdlr,
ready=self.ready,
verify=self.verify)
try:
server = self.rcvr(port=self.port, handler=self.hdlr,
ready=self.ready,
verify=self.verify)
except BaseException:
# Do not leave the caller waiting for ready forever.
self.ready.set()
raise
if self.port == 0:
self.port = server.server_address[1]
self.ready.set()
Expand Down
7 changes: 4 additions & 3 deletions Lib/logging/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -886,8 +886,9 @@ def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
"""
Initialize a handler.

If address is specified as a string, a UNIX socket is used. To log to a
local syslogd, "SysLogHandler(address="/dev/log")" can be used.
If address is specified as a string or bytes, a UNIX socket is used.
To log to a local syslogd, "SysLogHandler(address="/dev/log")" can be
used.
If facility is not specified, LOG_USER is used. If socktype is
specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
socket type will be used. For Unix sockets, you can also specify a
Expand Down Expand Up @@ -938,7 +939,7 @@ def createSocket(self):
address = self.address
socktype = self.socktype

if isinstance(address, str):
if not isinstance(address, (list, tuple)):
self.unixsocket = True
# Syslog server may be unavailable during handler initialisation.
# C's openlog() function also ignores connection errors.
Expand Down
42 changes: 42 additions & 0 deletions Lib/test/test_asyncio/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,48 @@ def reader(data):
self.loop.run_until_complete(proto.done)
self.assertEqual('CLOSED', proto.state)

@unittest.skipUnless(sys.platform != 'win32',
"Don't support pipes for Windows")
@unittest.skipUnless(hasattr(os, 'mkfifo'), 'requires os.mkfifo()')
def test_write_named_fifo_unread_data(self):
# gh-145030: on macOS, the write end of a named FIFO polls as
# readable while unread data sits in the FIFO, which made the
# transport misinterpret the event as the reader hanging up
# and close itself.
path = os_helper.TESTFN
os.mkfifo(path)
self.assertNotEqual(os.stat(path).st_nlink, 0)
self.addCleanup(os_helper.unlink, path)
rfd = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
self.addCleanup(os.close, rfd)
wfd = os.open(path, os.O_WRONLY | os.O_NONBLOCK)
pipeobj = io.open(wfd, 'wb', 1024)

proto = MyWritePipeProto(loop=self.loop)
connect = self.loop.connect_write_pipe(lambda: proto, pipeobj)
transport, p = self.loop.run_until_complete(connect)
self.assertIs(p, proto)
self.assertEqual('CONNECTED', proto.state)

transport.write(b'1')
# Iterate the event loop while the data stays unread in the FIFO;
# the transport must not detect a false disconnection.
for _ in range(10):
test_utils.run_briefly(self.loop)
self.assertEqual('CONNECTED', proto.state)
self.assertFalse(transport.is_closing())
self.assertEqual(b'1', os.read(rfd, 1024))

transport.write(b'2345')
for _ in range(10):
test_utils.run_briefly(self.loop)
self.assertEqual('CONNECTED', proto.state)
self.assertEqual(b'2345', os.read(rfd, 1024))

transport.close()
self.loop.run_until_complete(proto.done)
self.assertEqual('CLOSED', proto.state)

@unittest.skipUnless(sys.platform != 'win32',
"Don't support pipes for Windows")
def test_write_pipe_disconnect_on_close(self):
Expand Down
23 changes: 23 additions & 0 deletions Lib/test/test_asyncio/test_transports.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ def get_write_buffer_size(self):
self.assertTrue(transport._protocol_paused)
self.assertEqual(transport.get_write_buffer_limits(), (128, 256))

def test_flowcontrol_mixin_pause_writing_exception(self):

class MyTransport(transports._FlowControlMixin,
transports.Transport):

def get_write_buffer_size(self):
return 2000

loop = mock.Mock()
transport = MyTransport(loop=loop)
protocol = mock.Mock()
protocol.pause_writing.side_effect = RuntimeError("boom")
transport._protocol = protocol
transport.set_write_buffer_limits(high=1000, low=100)
transport._maybe_pause_protocol()
protocol.pause_writing.assert_called_once()
loop.call_exception_handler.assert_called_once()
args = loop.call_exception_handler.call_args[0][0]

self.assertIn("protocol.pause_writing() failed", args["message"])
self.assertIsInstance(args["exception"], RuntimeError)
self.assertTrue(transport._protocol_paused)

def test_flowcontrol_mixin_compute_write_limits(self):

class MyTransport(transports._FlowControlMixin,
Expand Down
26 changes: 26 additions & 0 deletions Lib/test/test_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2877,6 +2877,32 @@ class BEPoint:
self.assertEqual(m2.strides, (1,))
self.assertEqual(m2.suboffsets, ())

def test_memoryview_cast_f_contiguous_ND_1D(self):
nd = ndarray(list(range(12)), shape=[3, 4], format='B', flags=ND_FORTRAN)
m = memoryview(nd)
self.assertTrue(m.f_contiguous)
self.assertTrue(m.contiguous)

m1 = m.cast('B')
self.assertEqual(m1.ndim, 1)
self.assertEqual(m1.shape, (m.nbytes,))
self.assertEqual(m1.strides, (1,))
self.assertTrue(m1.c_contiguous)
self.assertTrue(m1.contiguous)
self.assertEqual(m1.tobytes(), memoryview(nd).tobytes(order='F'))

for fmt in ('B', 'b', 'c', 'H', 'I'):
size = struct.calcsize(fmt)
if m.nbytes % size == 0:
m2 = m.cast(fmt)
self.assertEqual(m2.ndim, 1)
self.assertEqual(m2.shape, (m.nbytes // size,))
self.assertTrue(m2.contiguous)

m3 = m[::-1]
with self.assertRaises(TypeError):
m3.cast('B')

def test_memoryview_tolist(self):

# Most tolist() tests are in self.verify() etc.
Expand Down
40 changes: 40 additions & 0 deletions Lib/test/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2226,6 +2226,46 @@ def __len__(self):

self.assertRaises(BufferError, ba.hex, S(b':'))

def test_no_init_called(self):
# A bytearray created without calling bytearray.__init__
# should not crash the interpreter (see gh-153419).
def bytearray_new():
return bytearray.__new__(bytearray)

bytearray_new().insert(0, 1)
bytearray_new().extend(b"x")
bytearray_new().extend([1, 2, 3])
bytearray_new().resize(4)
bytearray_new().__init__(5)
bytearray_new().__init__(b"xyz")
bytearray_new().take_bytes()
bytearray_new().take_bytes(0)

a = bytearray_new()
a.append(1)

a = bytearray_new()
a += b"x"

a = bytearray_new()
a[:] = b"xyz"

def test_reinit_length(self):
# There is a shortcut taken when resizing, where alloc/2 < newsize.
# In this case, the existing buffer is reused, rather than reset.
# If this happens when newsize == 0 and alloc == 1, then various
# code assumptions can be violated. This test should catch those
# in debug builds. (see gh-153419)
a = bytearray(1)
a.__init__()
self.assertEqual(a, b"")

def test_reinit_with_view(self):
a = bytearray()
with memoryview(a):
self.assertRaises(BufferError, a.__init__, "x", "ascii")
self.assertEqual(a, b"")


class AssortedBytesTest(unittest.TestCase):
#
Expand Down
Loading
Loading