From f2b8863691d9c28cb08efcb4dadceb3d09fc1efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Sun, 21 Dec 2025 06:25:56 +0100 Subject: [PATCH] Make exception/signal handlers idempotent Calling asyncio.Future.set_exception() or asyncio.Future.set_result() more than once for a given Future object raises an asyncio.InvalidStateError exception. In the case of AsyncServer: - it is enough to capture the first exception raised by higher-level logic as no exceptions at all are expected to be raised in the first place, - no distinction is made between SIGINT and SIGTERM; the only purpose of the signal handler is to make the server exit cleanly. Given the above, make both AsyncServer._handle_exception() and AsyncServer._signal_done() idempotent by ignoring asyncio.InvalidStateError exceptions raised by the relevant asyncio.Future.set_*() calls. (cherry picked from commit 0ec94e501a04dc6a0730d75e6eeab2782202e16b) --- bin/tests/system/isctest/asyncserver.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/bin/tests/system/isctest/asyncserver.py b/bin/tests/system/isctest/asyncserver.py index 98fec6b663..6ec3b00f10 100644 --- a/bin/tests/system/isctest/asyncserver.py +++ b/bin/tests/system/isctest/asyncserver.py @@ -198,7 +198,10 @@ class AsyncServer: ) -> None: assert self._work_done exception = context.get("exception", RuntimeError(context["message"])) - self._work_done.set_exception(exception) + try: + self._work_done.set_exception(exception) + except asyncio.InvalidStateError: + pass def _setup_signals(self) -> None: loop = self._get_asyncio_loop() @@ -207,7 +210,10 @@ class AsyncServer: def _signal_done(self) -> None: assert self._work_done - self._work_done.set_result(True) + try: + self._work_done.set_result(True) + except asyncio.InvalidStateError: + pass async def _listen_udp(self) -> None: if not self._udp_handler: