gh-155888: Fix asyncio writelines() hanging on an empty chunk (#155889)

kumaraditya303 <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/b2c299373d86b670f50d930564da22550af55308
commit: b2c299373d86b670f50d930564da22550af55308
branch: main
author: Timofei Ivankov <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-08-16T15:12:55+05:30
summary:

gh-155888: Fix asyncio writelines() hanging on an empty chunk (#155889)

files:
A Misc/NEWS.d/next/Library/2026-08-16-11-38-17.gh-issue-155888.pO-nAp.rst
M Lib/asyncio/selector_events.py
M Lib/test/test_asyncio/test_events.py

diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
index 19ffd8cc98b0e9d..83916160b9fbde9 100644
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -1198,8 +1198,13 @@ def writelines(self, list_of_data):
             return
 
         for data in list_of_data:
+            # gh-155888: an empty chunk can never be drained, so never buffer it
+            if not data:
+                continue
             self._buffer.append(memoryview(data))
             self._buffer_size += len(data)
+        if not self._buffer:
+            return
         self._write_ready()
         # If the entire buffer couldn't be written, register a write handler
         if self._buffer:
diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py
index f7cd59a54199710..db316fae090280a 100644
--- a/Lib/test/test_asyncio/test_events.py
+++ b/Lib/test/test_asyncio/test_events.py
@@ -560,6 +560,19 @@ def writer(data):
         r.close()
         self.assertEqual(read, data)
 
+    def test_writelines_empty_chunk(self):
+        # gh-155888: an empty chunk can never be drained, so never buffer it
+        rsock, wsock = socket.socketpair()
+        self.addCleanup(rsock.close)
+
+        async def main():
+            reader, writer = await asyncio.open_connection(sock=wsock)
+            writer.writelines([b'data', b''])
+            writer.close()
+            await asyncio.wait_for(writer.wait_closed(), support.SHORT_TIMEOUT)
+
+        self.loop.run_until_complete(main())
+
     @unittest.skipUnless(hasattr(signal, 'SIGKILL'), 'No SIGKILL')
     def test_add_signal_handler(self):
         caught = 0
diff --git a/Misc/NEWS.d/next/Library/2026-08-16-11-38-17.gh-issue-155888.pO-nAp.rst b/Misc/NEWS.d/next/Library/2026-08-16-11-38-17.gh-issue-155888.pO-nAp.rst
new file mode 100644
index 000000000000000..df3db71b912e1c2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-16-11-38-17.gh-issue-155888.pO-nAp.rst
@@ -0,0 +1,2 @@
+Fix :meth:`asyncio.WriteTransport.writelines` hanging the transport when the
+last data chunk is empty.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.