]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-103462: Ensure SelectorSocketTransport.writelines registers a writer when data...
authorAli-Akber Saifee <ali@indydevs.org>
Thu, 13 Apr 2023 04:46:52 +0000 (21:46 -0700)
committerGitHub <noreply@github.com>
Thu, 13 Apr 2023 04:46:52 +0000 (10:16 +0530)
Lib/asyncio/selector_events.py
Lib/test/test_asyncio/test_selector_events.py
Misc/NEWS.d/next/Library/2023-04-12-06-00-02.gh-issue-103462.w6yBlM.rst [new file with mode: 0644]

index de5076a96218e0f4f0e60635258a8be04a51f1c0..3a697129e4c914f57c251cdf27696c22995f0f1c 100644 (file)
@@ -1176,6 +1176,9 @@ class _SelectorSocketTransport(_SelectorTransport):
             return
         self._buffer.extend([memoryview(data) for data in list_of_data])
         self._write_ready()
+        # If the entire buffer couldn't be written, register a write handler
+        if self._buffer:
+            self._loop._add_writer(self._sock_fd, self._write_ready)
 
     def can_write_eof(self):
         return True
index 921c98a2702d76576a4a634174f85ed4ca718bbb..e41341fd26e19eeebbf51e0a0627b81aff89ca1b 100644 (file)
@@ -747,6 +747,48 @@ class SelectorSocketTransportTests(test_utils.TestCase):
         self.assertFalse(self.sock.sendmsg.called)
         self.assertEqual(list_to_buffer([b'data']), transport._buffer)
 
+    @unittest.skipUnless(selector_events._HAS_SENDMSG, 'no sendmsg')
+    def test_writelines_sendmsg_full(self):
+        data = memoryview(b'data')
+        self.sock.sendmsg = mock.Mock()
+        self.sock.sendmsg.return_value = len(data)
+
+        transport = self.socket_transport(sendmsg=True)
+        transport.writelines([data])
+        self.assertTrue(self.sock.sendmsg.called)
+        self.assertFalse(self.loop.writers)
+
+    @unittest.skipUnless(selector_events._HAS_SENDMSG, 'no sendmsg')
+    def test_writelines_sendmsg_partial(self):
+        data = memoryview(b'data')
+        self.sock.sendmsg = mock.Mock()
+        self.sock.sendmsg.return_value = 2
+
+        transport = self.socket_transport(sendmsg=True)
+        transport.writelines([data])
+        self.assertTrue(self.sock.sendmsg.called)
+        self.assertTrue(self.loop.writers)
+
+    def test_writelines_send_full(self):
+        data = memoryview(b'data')
+        self.sock.send.return_value = len(data)
+        self.sock.send.fileno.return_value = 7
+
+        transport = self.socket_transport()
+        transport.writelines([data])
+        self.assertTrue(self.sock.send.called)
+        self.assertFalse(self.loop.writers)
+
+    def test_writelines_send_partial(self):
+        data = memoryview(b'data')
+        self.sock.send.return_value = 2
+        self.sock.send.fileno.return_value = 7
+
+        transport = self.socket_transport()
+        transport.writelines([data])
+        self.assertTrue(self.sock.send.called)
+        self.assertTrue(self.loop.writers)
+
     @unittest.skipUnless(selector_events._HAS_SENDMSG, 'no sendmsg')
     def test_write_sendmsg_full(self):
         data = memoryview(b'data')
diff --git a/Misc/NEWS.d/next/Library/2023-04-12-06-00-02.gh-issue-103462.w6yBlM.rst b/Misc/NEWS.d/next/Library/2023-04-12-06-00-02.gh-issue-103462.w6yBlM.rst
new file mode 100644 (file)
index 0000000..50758c8
--- /dev/null
@@ -0,0 +1,4 @@
+Fixed an issue with using :meth:`~asyncio.WriteTransport.writelines` in :mod:`asyncio` to send very
+large payloads that exceed the amount of data that can be written in one
+call to :meth:`socket.socket.send` or :meth:`socket.socket.sendmsg`,
+resulting in the remaining buffer being left unwritten.