From d2eca9506ef2627108637be14e60295cc2088bc0 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 23 Oct 2022 19:46:26 -0700 Subject: [PATCH 1/3] Fix asyncio.Server.wait_closed() It was a no-op when used as recommended (after close()). I had to debug one test (test__sock_sendfile_native_failure) -- the cleanup sequence for the test fixture was botched. Hopefully that's not a portend of problems in user code -- this has never worked so people may well be doing this wrong. :-( --- Lib/asyncio/base_events.py | 2 +- Lib/test/test_asyncio/test_base_events.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index c8a2f9f25634ef..254dd745df2c2a 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -377,7 +377,7 @@ async def serve_forever(self): self._serving_forever_fut = None async def wait_closed(self): - if self._sockets is None or self._waiters is None: + if self._waiters is None or self._active_count == 0: return waiter = self._loop.create_future() self._waiters.append(waiter) diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 2dcb20c1cec7f9..7421d18dc636c8 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -2052,11 +2052,11 @@ def prepare(self): def cleanup(): server.close() - self.run_loop(server.wait_closed()) sock.close() if proto.transport is not None: proto.transport.close() self.run_loop(proto.wait_closed()) + self.run_loop(server.wait_closed()) self.addCleanup(cleanup) From a9aebfccc444630f4001a7c44b0bb5c641fdfe61 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 22 Nov 2022 19:31:30 +0000 Subject: [PATCH 2/3] =?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/2022-11-22-19-31-26.gh-issue-79033.MW6kHq.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2022-11-22-19-31-26.gh-issue-79033.MW6kHq.rst diff --git a/Misc/NEWS.d/next/Library/2022-11-22-19-31-26.gh-issue-79033.MW6kHq.rst b/Misc/NEWS.d/next/Library/2022-11-22-19-31-26.gh-issue-79033.MW6kHq.rst new file mode 100644 index 00000000000000..4b12fd9c8d798f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-22-19-31-26.gh-issue-79033.MW6kHq.rst @@ -0,0 +1 @@ +Fix :func:`asyncio.Server.wait_closed` to actually do what the docs promise -- wait for all existing connections to complete, after closing the server. From 0b4057a2ea66bd92e74a33f93d88a820571fe86b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 23 Nov 2022 14:45:35 -0800 Subject: [PATCH 3/3] Add a test (most of it contributed by Kumar) --- Lib/test/test_asyncio/test_server.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Lib/test/test_asyncio/test_server.py b/Lib/test/test_asyncio/test_server.py index 860d62d52ef1ea..06d8b60f219f1a 100644 --- a/Lib/test/test_asyncio/test_server.py +++ b/Lib/test/test_asyncio/test_server.py @@ -120,6 +120,33 @@ async def main(srv): self.loop.run_until_complete(srv.serve_forever()) +class TestServer2(unittest.IsolatedAsyncioTestCase): + + async def test_wait_closed(self): + async def serve(*args): + pass + + srv = await asyncio.start_server(serve, socket_helper.HOSTv4, 0) + + # active count = 0 + task1 = asyncio.create_task(srv.wait_closed()) + await asyncio.sleep(0) + self.assertTrue(task1.done()) + + # active count != 0 + srv._attach() + task2 = asyncio.create_task(srv.wait_closed()) + await asyncio.sleep(0) + self.assertFalse(task2.done()) + + srv.close() + await asyncio.sleep(0) + self.assertFalse(task2.done()) + + srv._detach() + await task2 + + @unittest.skipUnless(hasattr(asyncio, 'ProactorEventLoop'), 'Windows only') class ProactorStartServerTests(BaseStartServer, unittest.TestCase):