Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork32.4k
Description
Bug report
Bug description:
SelectorSocketTransport (in asyncio.selector_events) implements bothwrite
andwritelines
, 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 betweenwrite
andwritelines
(write
would drop the data on the floor and possibly print a warning, whilewritelines
would append it to the buffer).
I would suggest modifyingwritelines
to use the same check for_conn_lost
aswrite
. Let me know if you'd like a PR.
The code below gives either anAttributeError
orTypeError
(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 python3importasyncioimportfunctoolsasyncdefclient_connected_cb(event:asyncio.Event,reader:asyncio.StreamReader,writer:asyncio.StreamWriter)->None:awaitevent.wait()# Waits for the client side to disconnectwriter.write(b"foo")writer.write(b"spam")# Linux seems to fail the second write but not the firstawaitasyncio.sleep(0)print(writer.transport._conn_lost)# Should be 1writer.writelines([b"baz"])# Replace above line with this for comparison:# writer.write(b"baz")awaitwriter.drain()writer.close()awaitwriter.wait_closed()asyncdefmain():event=asyncio.Event()server=awaitasyncio.start_server(functools.partial(client_connected_cb,event),host="127.0.0.1",port=8888, )# Establish a connection, then close itclient_reader,client_writer=awaitasyncio.open_connection("127.0.0.1",8888)client_writer.close()awaitclient_writer.wait_closed()event.set()# Wake up client_connected_cb# Cleanupserver.close()awaitserver.wait_closed()if__name__=="__main__":asyncio.run(main())
Output (3.12):
1Unhandled exception in client_connected_cbtransport: <_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):
1Unhandled exception in client_connected_cbtransport: <_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 implementwritelines
in SelectorSocketTransport; they rely on the generic version that just concatenates the messages and passes them towrite
.
CPython versions tested on:
3.13, 3.14, 3.12
Operating systems tested on:
Linux
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status