From 24f14e903e28a6da149f3354428550909362808b Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sun, 1 Sep 2024 20:31:00 +0100 Subject: [PATCH] Avoid logging exception from run_app() that is also raised (#8951) (cherry picked from commit 45d6e4f14572bfc4cfc3f32a2c7c72a9cc28f125) --- CHANGES/6807.bugfix.rst | 1 + aiohttp/web.py | 13 +++++++++---- tests/test_run_app.py | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 CHANGES/6807.bugfix.rst diff --git a/CHANGES/6807.bugfix.rst b/CHANGES/6807.bugfix.rst new file mode 100644 index 00000000000..4eb07b9e0da --- /dev/null +++ b/CHANGES/6807.bugfix.rst @@ -0,0 +1 @@ +Stopped logging exceptions from ``web.run_app()`` that would be raised regardless -- by :user:`Dreamsorcerer`. diff --git a/aiohttp/web.py b/aiohttp/web.py index 8708f1fcbec..88bf14bf828 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -6,6 +6,7 @@ import warnings from argparse import ArgumentParser from collections.abc import Iterable +from contextlib import suppress from importlib import import_module from typing import ( Any, @@ -519,10 +520,14 @@ def run_app( except (GracefulExit, KeyboardInterrupt): # pragma: no cover pass finally: - _cancel_tasks({main_task}, loop) - _cancel_tasks(asyncio.all_tasks(loop), loop) - loop.run_until_complete(loop.shutdown_asyncgens()) - loop.close() + try: + main_task.cancel() + with suppress(asyncio.CancelledError): + loop.run_until_complete(main_task) + finally: + _cancel_tasks(asyncio.all_tasks(loop), loop) + loop.run_until_complete(loop.shutdown_asyncgens()) + loop.close() def main(argv: List[str]) -> None: diff --git a/tests/test_run_app.py b/tests/test_run_app.py index c1d5f8e14f4..305f6d4ca7f 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -906,6 +906,23 @@ async def init(): assert count == 3 +def test_run_app_raises_exception(patched_loop: asyncio.AbstractEventLoop) -> None: + async def context(app: web.Application) -> AsyncIterator[None]: + raise RuntimeError("foo") + yield # pragma: no cover + + app = web.Application() + app.cleanup_ctx.append(context) + + with mock.patch.object( + patched_loop, "call_exception_handler", autospec=True, spec_set=True + ) as m: + with pytest.raises(RuntimeError, match="foo"): + web.run_app(app, loop=patched_loop) + + assert not m.called + + class TestShutdown: def raiser(self) -> NoReturn: raise KeyboardInterrupt