From 5a717036ad9ff7b86efd44943e998a2afe0c80d5 Mon Sep 17 00:00:00 2001 From: DPR Date: Wed, 15 Nov 2023 09:17:51 +0800 Subject: [PATCH 1/2] [3.12] gh-109538: Avoid RuntimeError when StreamWriter is deleted with closed loop (GH-111983) Issue a ResourceWarning instead. (cherry picked from commit e0f512797596282bff63260f8102592aad37cdf1) gh-109538: Avoid RuntimeError when StreamWriter is deleted with closed loop (#111983) Issue a ResourceWarning instead. Co-authored-by: Hugo van Kemenade (cherry picked from commit e0f512797596282bff63260f8102592aad37cdf1) --- Lib/asyncio/streams.py | 7 ++- Lib/test/test_asyncio/test_streams.py | 59 +++++++++++++++++++ ...-11-11-16-42-48.gh-issue-109538.cMG5ux.rst | 1 + 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index f63eeca2a7719a..2b39048912d04d 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -405,8 +405,11 @@ async def start_tls(self, sslcontext, *, def __del__(self): if not self._transport.is_closing(): - self.close() - + if self._loop.is_closed(): + warnings.warn("loop is closed", ResourceWarning) + else: + self.close() + warnings.warn(f"unclosed {self!r}", ResourceWarning) class StreamReader: diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 5a22232c00a36a..ccb7dbf667c320 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -1073,6 +1073,65 @@ def test_eof_feed_when_closing_writer(self): self.assertEqual(messages, []) + def test_unclosed_resource_warnings(self): + async def inner(httpd): + rd, wr = await asyncio.open_connection(*httpd.address) + + wr.write(b'GET / HTTP/1.0\r\n\r\n') + data = await rd.readline() + self.assertEqual(data, b'HTTP/1.0 200 OK\r\n') + data = await rd.read() + self.assertTrue(data.endswith(b'\r\n\r\nTest message')) + 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 None: port = socket_helper.find_unused_port() diff --git a/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst b/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst new file mode 100644 index 00000000000000..d1ee4c054a3f19 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst @@ -0,0 +1 @@ +Issue warning message instead of having :class:`RuntimeError` be displayed when event loop has already been closed at :meth:`StreamWriter.__del__`. From 15e8b1fd8fcf2fcd79753f06190e30aa24dfed39 Mon Sep 17 00:00:00 2001 From: DPR Date: Thu, 16 Nov 2023 15:43:48 +0800 Subject: [PATCH 2/2] Fix missing warnings import --- Lib/asyncio/streams.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 2b39048912d04d..f5695266e1a8c6 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -5,6 +5,7 @@ import collections import socket import sys +import warnings import weakref if hasattr(socket, 'AF_UNIX'):