From 354780aea26261dde4141603c887f8067477f993 Mon Sep 17 00:00:00 2001 From: DPR Date: Sun, 12 Nov 2023 00:00:16 +0800 Subject: [PATCH 1/6] gh-109538: Catch closed loop runtime error and issue warning --- Lib/asyncio/streams.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index f82b10c3803656..15d86dbf682bee 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -406,9 +406,12 @@ async def start_tls(self, sslcontext, *, def __del__(self, warnings=warnings): if not self._transport.is_closing(): - self.close() - warnings.warn(f"unclosed {self!r}", ResourceWarning) - + try: + self.close() + except RuntimeError: + warnings.warn("loop is closed", ResourceWarning) + else: + warnings.warn(f"unclosed {self!r}", ResourceWarning) class StreamReader: From 0a1c4ef3028e045f8aa54a59240e49bb73656cb8 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sat, 11 Nov 2023 16:42:49 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst 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..5c0d54851916d1 --- /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 :method:`StreamWriter.__del__` From 7461e4e259f48e0698176798ffc8003d43fa5d05 Mon Sep 17 00:00:00 2001 From: DPR Date: Sun, 12 Nov 2023 09:56:08 +0800 Subject: [PATCH 3/6] Update Misc/NEWS.d/next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst Co-authored-by: Hugo van Kemenade --- .../next/Library/2023-11-11-16-42-48.gh-issue-109538.cMG5ux.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 5c0d54851916d1..d1ee4c054a3f19 100644 --- 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 @@ -1 +1 @@ -Issue warning message instead of having :class:`RuntimeError` be displayed when event loop has already been closed at :method:`StreamWriter.__del__` +Issue warning message instead of having :class:`RuntimeError` be displayed when event loop has already been closed at :meth:`StreamWriter.__del__`. From 54106eb2f5d5d24e781967787527d0a2a9f0b49a Mon Sep 17 00:00:00 2001 From: DPR Date: Tue, 14 Nov 2023 11:32:48 +0800 Subject: [PATCH 4/6] use checking condition instead of try catch --- Lib/asyncio/streams.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index 15d86dbf682bee..ffb48b9cd6cb70 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -406,11 +406,10 @@ async def start_tls(self, sslcontext, *, def __del__(self, warnings=warnings): if not self._transport.is_closing(): - try: - self.close() - except RuntimeError: + if self._loop.is_closed(): warnings.warn("loop is closed", ResourceWarning) else: + self.close() warnings.warn(f"unclosed {self!r}", ResourceWarning) class StreamReader: From b088f8200f60ee4cd49f273c918b6e6abf803400 Mon Sep 17 00:00:00 2001 From: DPR Date: Tue, 14 Nov 2023 15:24:25 +0800 Subject: [PATCH 5/6] Add test --- Lib/test/test_asyncio/test_streams.py | 38 +++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 3fea7b9f6911aa..a50487283c2168 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -1082,10 +1082,11 @@ async def inner(httpd): 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): + 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() From 5eb3fa196a120264c90fdb0777db87fc6500d8d1 Mon Sep 17 00:00:00 2001 From: DPR Date: Wed, 15 Nov 2023 08:54:56 +0800 Subject: [PATCH 6/6] Make test more clear --- Lib/test/test_asyncio/test_streams.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index a50487283c2168..ccb7dbf667c320 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -1105,9 +1105,11 @@ async def inner(httpd): 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')) + + # Make "loop is closed" occur first before "del wr" for this test. self.loop.stop() wr.close() - while not wr._loop.is_closed(): + while not self.loop.is_closed(): await asyncio.sleep(0.0) with self.assertWarns(ResourceWarning) as cm: @@ -1122,6 +1124,7 @@ async def inner(httpd): with test_utils.run_test_server() as httpd: try: self.loop.run_until_complete(inner(httpd)) + # This exception is caused by `self.loop.stop()` as expected. except RuntimeError: pass finally: