diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 6ad84044adf146..cbe011571acef5 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -1077,7 +1077,11 @@ def write(self, data): self._fatal_error(exc, 'Fatal write error on socket transport') return else: - data = memoryview(data)[n:] + if isinstance(data, memoryview): + data = data.cast('c')[n:] + else: + data = memoryview(data)[n:] + assert(data.itemsize == 1) if not data: return # Not all was written; register write handler. diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index aab6a779170eb9..21233d1f38031a 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -1,5 +1,6 @@ """Tests for selector_events.py""" +import array import collections import selectors import socket @@ -13,6 +14,11 @@ except ImportError: ssl = None +try: + from _testbuffer import * +except ImportError: + ndarray = None + import asyncio from asyncio.selector_events import (BaseSelectorEventLoop, _SelectorDatagramTransport, @@ -768,7 +774,39 @@ def test_write_partial_memoryview(self): transport.write(data) self.loop.assert_writer(7, transport._write_ready) - self.assertEqual(list_to_buffer([b'ta']), transport._buffer) + self.assertEqualBufferContents(b'ta', transport._buffer) + + def test_write_partial_nonbyte_array_memview_write(self): + arr = array.array('l', [-1, 1]) + data = memoryview(arr) + + self.sock.send.return_value = len(arr) * data.itemsize // 2 + + transport = self.socket_transport() + transport.write(data) + + self.loop.assert_writer(7, transport._write_ready) + remainder = memoryview(array.array('l', [1])) + self.assertEqualBufferContents(remainder.tobytes(), transport._buffer) + + @unittest.skipUnless(ndarray, 'ndarray object required for this test') + def test_write_partial_ndarray_memview_write(self): + items = (-2, -1, 1, 2) + arr = ndarray(items, format='l', shape=(1, 2, 2)) + data = memoryview(arr) + + self.sock.send.return_value = arr.nbytes // 2 + + transport = self.socket_transport() + transport.write(data) + + self.loop.assert_writer(7, transport._write_ready) + remainder = memoryview(array.array('l', (1, 2))) + self.assertEqualBufferContents(remainder.tobytes(), transport._buffer) + + def assertEqualBufferContents(self, expected, buffer): + self.assertEqual(len(buffer), 1) + self.assertEqual(expected, buffer[0].tobytes()) def test_write_partial_none(self): data = b'data' diff --git a/Misc/NEWS.d/next/Library/2025-06-26-22-12-13.gh-issue-135862.wYKSKx.rst b/Misc/NEWS.d/next/Library/2025-06-26-22-12-13.gh-issue-135862.wYKSKx.rst new file mode 100644 index 00000000000000..c3f4749095699d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-26-22-12-13.gh-issue-135862.wYKSKx.rst @@ -0,0 +1,2 @@ +Fix bug where partial writes of :class:`memoryview` s of non-byte or 2+ +dimensional arrays would write remaining binary data incorrectly.