Skip to content

Commit b2c2993

Browse files
authored
gh-155888: Fix asyncio writelines() hanging on an empty chunk (#155889)
1 parent c144799 commit b2c2993

3 files changed

Lines changed: 20 additions & 0 deletions

File tree

Lib/asyncio/selector_events.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,8 +1198,13 @@ def writelines(self, list_of_data):
11981198
return
11991199

12001200
for data in list_of_data:
1201+
# gh-155888: an empty chunk can never be drained, so never buffer it
1202+
if not data:
1203+
continue
12011204
self._buffer.append(memoryview(data))
12021205
self._buffer_size += len(data)
1206+
if not self._buffer:
1207+
return
12031208
self._write_ready()
12041209
# If the entire buffer couldn't be written, register a write handler
12051210
if self._buffer:

Lib/test/test_asyncio/test_events.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,19 @@ def writer(data):
560560
r.close()
561561
self.assertEqual(read, data)
562562

563+
def test_writelines_empty_chunk(self):
564+
# gh-155888: an empty chunk can never be drained, so never buffer it
565+
rsock, wsock = socket.socketpair()
566+
self.addCleanup(rsock.close)
567+
568+
async def main():
569+
reader, writer = await asyncio.open_connection(sock=wsock)
570+
writer.writelines([b'data', b''])
571+
writer.close()
572+
await asyncio.wait_for(writer.wait_closed(), support.SHORT_TIMEOUT)
573+
574+
self.loop.run_until_complete(main())
575+
563576
@unittest.skipUnless(hasattr(signal, 'SIGKILL'), 'No SIGKILL')
564577
def test_add_signal_handler(self):
565578
caught = 0
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :meth:`asyncio.WriteTransport.writelines` hanging the transport when the
2+
last data chunk is empty.

0 commit comments

Comments
 (0)