SelectorSocketTransport.writelines does not protect against connection lost · Issue #136234 · python/cpython · GitHub | Latest TMZ Celebrity News & Gossip | Watch TMZ Live
Skip to content

SelectorSocketTransport.writelines does not protect against connection lost #136234

Open
@bmerry

Description

@bmerry

Bug report

Bug description:

SelectorSocketTransport (in asyncio.selector_events) implements both write and writelines, but only the first has handling for the case that the connection has already been lost. That in turn can lead to dereferencing a None object, because _call_connection_lost clears a number of attributes. Even if that were fixed, it also means that there would be inconsistent behaviour between write and writelines (write would drop the data on the floor and possibly print a warning, while writelines would append it to the buffer).

I would suggest modifying writelines to use the same check for _conn_lost as write. Let me know if you'd like a PR.

The code below gives either an AttributeError or TypeError (depending on Python version) on my machine (Ubuntu 24.04, 6.8.0-63-generic). It may depend on corner cases of when exactly the kernel notifies a TCP sender that the other side has closed the connection and hence might not be a totally reliable reproducer.

#!/usr/bin/env python3

import asyncio
import functools


async def client_connected_cb(event: asyncio.Event, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
    await event.wait()  # Waits for the client side to disconnect
    writer.write(b"foo")
    writer.write(b"spam")  # Linux seems to fail the second write but not the first
    await asyncio.sleep(0)
    print(writer.transport._conn_lost)  # Should be 1
    writer.writelines([b"baz"])
    # Replace above line with this for comparison:
    # writer.write(b"baz")
    await writer.drain()
    writer.close()
    await writer.wait_closed()


async def main():
    event = asyncio.Event()
    server = await asyncio.start_server(
        functools.partial(client_connected_cb, event),
        host="127.0.0.1",
        port=8888,
    )
    # Establish a connection, then close it
    client_reader, client_writer = await asyncio.open_connection("127.0.0.1", 8888)
    client_writer.close()
    await client_writer.wait_closed()
    event.set()  # Wake up client_connected_cb
    # Cleanup
    server.close()
    await server.wait_closed()


if __name__ == "__main__":
    asyncio.run(main())

Output (3.12):

1
Unhandled exception in client_connected_cb
transport: <_SelectorSocketTransport closed fd=8>
Traceback (most recent call last):
  File "/home/bmerry/work/experiments/asyncio-writelines/./writelines_hang.py", line 13, in client_connected_cb
    writer.writelines([b"baz"])
  File "/usr/lib/python3.12/asyncio/streams.py", line 349, in writelines
    self._transport.writelines(data)
  File "/usr/lib/python3.12/asyncio/selector_events.py", line 1185, in writelines
    self._loop._add_writer(self._sock_fd, self._write_ready)
    ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute '_add_writer'

Output (3.13, 3.14):

1
Unhandled exception in client_connected_cb
transport: <_SelectorSocketTransport closed fd=8>
Traceback (most recent call last):
  File "/home/bmerry/work/experiments/asyncio-writelines/./writelines_hang.py", line 13, in client_connected_cb
    writer.writelines([b"baz"])
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/home/bmerry/.pyenv/versions/3.14-dev/lib/python3.14/asyncio/streams.py", line 343, in writelines
    self._transport.writelines(data)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/home/bmerry/.pyenv/versions/3.14-dev/lib/python3.14/asyncio/selector_events.py", line 1178, in writelines
    self._write_ready()
    ~~~~~~~~~~~~~~~~~^^
TypeError: 'NoneType' object is not callable

Note that 3.11 and earlier are not affected because they do not implement writelines in SelectorSocketTransport; they rely on the generic version that just concatenates the messages and passes them to write.

CPython versions tested on:

3.13, 3.14, 3.12

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.13bugs and security fixes3.14bugs and security fixes3.15new features, bugs and security fixesstdlibPython modules in the Lib dirtopic-asynciotype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      TMZ Celebrity News – Breaking Stories, Videos & Gossip

      Looking for the latest TMZ celebrity news? You've come to the right place. From shocking Hollywood scandals to exclusive videos, TMZ delivers it all in real time.

      Whether it’s a red carpet slip-up, a viral paparazzi moment, or a legal drama involving your favorite stars, TMZ news is always first to break the story. Stay in the loop with daily updates, insider tips, and jaw-dropping photos.

      🎥 Watch TMZ Live

      TMZ Live brings you daily celebrity news and interviews straight from the TMZ newsroom. Don’t miss a beat—watch now and see what’s trending in Hollywood.