From a355f60b032306651ca27bc53bbb82eb5106ff71 Mon Sep 17 00:00:00 2001 From: "Pierre Ossman (ThinLinc team)" Date: Wed, 28 Feb 2024 02:27:44 +0100 Subject: gh-114914: Avoid keeping dead StreamWriter alive (#115661) In some cases we might cause a StreamWriter to stay alive even when the application has dropped all references to it. This prevents us from doing automatical cleanup, and complaining that the StreamWriter wasn't properly closed. Fortunately, the extra reference was never actually used for anything so we can just drop it. --- Lib/asyncio/streams.py | 14 ++++-------- Lib/test/test_asyncio/test_streams.py | 25 ++++++++++++++++++++++ .../2024-02-19-15-52-30.gh-issue-114914.M5-1d8.rst | 2 ++ 3 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-02-19-15-52-30.gh-issue-114914.M5-1d8.rst diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index df58b7a..3fe52db 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -201,7 +201,6 @@ class StreamReaderProtocol(FlowControlMixin, protocols.Protocol): # is established. self._strong_reader = stream_reader self._reject_connection = False - self._stream_writer = None self._task = None self._transport = None self._client_connected_cb = client_connected_cb @@ -214,10 +213,8 @@ class StreamReaderProtocol(FlowControlMixin, protocols.Protocol): return None return self._stream_reader_wr() - def _replace_writer(self, writer): + def _replace_transport(self, transport): loop = self._loop - transport = writer.transport - self._stream_writer = writer self._transport = transport self._over_ssl = transport.get_extra_info('sslcontext') is not None @@ -239,11 +236,8 @@ class StreamReaderProtocol(FlowControlMixin, protocols.Protocol): reader.set_transport(transport) self._over_ssl = transport.get_extra_info('sslcontext') is not None if self._client_connected_cb is not None: - self._stream_writer = StreamWriter(transport, self, - reader, - self._loop) - res = self._client_connected_cb(reader, - self._stream_writer) + writer = StreamWriter(transport, self, reader, self._loop) + res = self._client_connected_cb(reader, writer) if coroutines.iscoroutine(res): def callback(task): if task.cancelled(): @@ -405,7 +399,7 @@ class StreamWriter: ssl_handshake_timeout=ssl_handshake_timeout, ssl_shutdown_timeout=ssl_shutdown_timeout) self._transport = new_transport - protocol._replace_writer(self) + protocol._replace_transport(new_transport) def __del__(self, warnings=warnings): if not self._transport.is_closing(): diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 2109905..bf123eb 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -1130,6 +1130,31 @@ os.close(fd) self.assertEqual(messages, []) + def test_unclosed_server_resource_warnings(self): + async def inner(rd, wr): + fut.set_result(True) + with self.assertWarns(ResourceWarning) as cm: + del wr + gc.collect() + self.assertEqual(len(cm.warnings), 1) + self.assertTrue(str(cm.warnings[0].message).startswith("unclosed