From 99d1c7fa855af34c83197de310131b4d54c3a76e Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 30 Mar 2021 16:55:34 +0100 Subject: [PATCH 01/17] Add test for run_app() loop error. --- tests/test_web_runner.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index 16a7285e2ab..7c97a72d8f1 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -211,3 +211,22 @@ async def mock_create_server(*args, **kwargs): assert server is runner.server assert host is None assert port == 8080 + + +async def test_run_after_asyncio_run() -> None: + async def nothing(): + pass + + async def shutdown(): + raise web.GracefulExit() + + # asyncio.run() creates a new loop and closes it. + asyncio.run(nothing()) + + app = web.Application() + # create_task() will delay the function until app is run. + app.on_startup.append(lambda a: asyncio.create_task(shutdown())) + try: + web.run_app(app) + except RuntimeError: + pytest.fail("run_app() should work after asyncio.run().") From c3e8c3c8195ebd10b417137b75a51bfbf5bd05ab Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 30 Mar 2021 17:04:46 +0100 Subject: [PATCH 02/17] Non-async test --- tests/test_web_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index 7c97a72d8f1..865dc35a2bc 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -213,7 +213,7 @@ async def mock_create_server(*args, **kwargs): assert port == 8080 -async def test_run_after_asyncio_run() -> None: +def test_run_after_asyncio_run() -> None: async def nothing(): pass From 2049e21010f31bb4fd9a7ab385108de7d76c5497 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 30 Mar 2021 17:13:14 +0100 Subject: [PATCH 03/17] Use new_event_loop() --- aiohttp/web.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aiohttp/web.py b/aiohttp/web.py index 6521e29e1c8..94788cd0e04 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -479,7 +479,8 @@ def run_app( reuse_port: Optional[bool] = None, ) -> None: """Run an app locally""" - loop = asyncio.get_event_loop() + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) loop.set_debug(debug) # Configure if and only if in debugging mode and using the default logger From 13ae73400ea154e7ddc3a624562389be82d32918 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 30 Mar 2021 17:14:50 +0100 Subject: [PATCH 04/17] Create 5572.bugfix --- CHANGES/5572.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGES/5572.bugfix diff --git a/CHANGES/5572.bugfix b/CHANGES/5572.bugfix new file mode 100644 index 00000000000..660bde2af12 --- /dev/null +++ b/CHANGES/5572.bugfix @@ -0,0 +1 @@ +Always create a new event loop in run_app() to avoid an error caused by using `asyncio.run()` beforehand. From 0c79ed3a4692bedd59e4af6af1926c020428baba Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Tue, 30 Mar 2021 21:36:59 +0100 Subject: [PATCH 05/17] Update 5572.bugfix --- CHANGES/5572.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/5572.bugfix b/CHANGES/5572.bugfix index 660bde2af12..1d481a0e3fb 100644 --- a/CHANGES/5572.bugfix +++ b/CHANGES/5572.bugfix @@ -1 +1 @@ -Always create a new event loop in run_app() to avoid an error caused by using `asyncio.run()` beforehand. +Always create a new event loop in run_app() to avoid an error caused by using ``asyncio.run()`` beforehand. From f7ee1043b5b015338f22395b2a64420fbd5eafbd Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 31 Mar 2021 10:17:06 +0100 Subject: [PATCH 06/17] Move set_event_loop() into try block (to match asyncio.run()) --- aiohttp/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aiohttp/web.py b/aiohttp/web.py index 94788cd0e04..532351ccda4 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -480,7 +480,6 @@ def run_app( ) -> None: """Run an app locally""" loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) loop.set_debug(debug) # Configure if and only if in debugging mode and using the default logger @@ -491,6 +490,7 @@ def run_app( access_log.addHandler(logging.StreamHandler()) try: + asyncio.set_event_loop(loop) main_task = loop.create_task( _run_app( app, From dce79e005ca74d9bde1e2da71d39fb55207a6fd7 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Wed, 31 Mar 2021 12:31:32 +0100 Subject: [PATCH 07/17] Update CHANGES/5572.bugfix Co-authored-by: Sviatoslav Sydorenko --- CHANGES/5572.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/5572.bugfix b/CHANGES/5572.bugfix index 1d481a0e3fb..9c3bcdc8980 100644 --- a/CHANGES/5572.bugfix +++ b/CHANGES/5572.bugfix @@ -1 +1 @@ -Always create a new event loop in run_app() to avoid an error caused by using ``asyncio.run()`` beforehand. +Always create a new event loop in ``aiohttp.web.run_app()`` to avoid an error caused by using ``asyncio.run()`` beforehand. From a2aa0a997159b25a9a0b28b23bbe7f6344db6249 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 12:09:59 +0100 Subject: [PATCH 08/17] Move create_task() before try. --- aiohttp/web.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/aiohttp/web.py b/aiohttp/web.py index 532351ccda4..9948730e7ba 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -489,28 +489,29 @@ def run_app( if not access_log.hasHandlers(): access_log.addHandler(logging.StreamHandler()) + main_task = loop.create_task( + _run_app( + app, + host=host, + port=port, + path=path, + sock=sock, + shutdown_timeout=shutdown_timeout, + keepalive_timeout=keepalive_timeout, + ssl_context=ssl_context, + print=print, + backlog=backlog, + access_log_class=access_log_class, + access_log_format=access_log_format, + access_log=access_log, + handle_signals=handle_signals, + reuse_address=reuse_address, + reuse_port=reuse_port, + ) + ) + try: asyncio.set_event_loop(loop) - main_task = loop.create_task( - _run_app( - app, - host=host, - port=port, - path=path, - sock=sock, - shutdown_timeout=shutdown_timeout, - keepalive_timeout=keepalive_timeout, - ssl_context=ssl_context, - print=print, - backlog=backlog, - access_log_class=access_log_class, - access_log_format=access_log_format, - access_log=access_log, - handle_signals=handle_signals, - reuse_address=reuse_address, - reuse_port=reuse_port, - ) - ) loop.run_until_complete(main_task) except (GracefulExit, KeyboardInterrupt): # pragma: no cover pass From 870dddabb55784566244ab33613a72dd66cebfea Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 12:22:53 +0100 Subject: [PATCH 09/17] Add explicit loop parameter. --- aiohttp/web.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aiohttp/web.py b/aiohttp/web.py index 9948730e7ba..0062076268f 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -477,9 +477,11 @@ def run_app( handle_signals: bool = True, reuse_address: Optional[bool] = None, reuse_port: Optional[bool] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, ) -> None: """Run an app locally""" - loop = asyncio.new_event_loop() + if loop is None: + loop = asyncio.new_event_loop() loop.set_debug(debug) # Configure if and only if in debugging mode and using the default logger From 1d89276ce8159da6653040e00d20494947311874 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 12:27:20 +0100 Subject: [PATCH 10/17] Use patched loop in run_app() --- tests/test_run_app.py | 56 +++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/tests/test_run_app.py b/tests/test_run_app.py index f63344a323f..f04550e726b 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -94,7 +94,7 @@ def test_run_app_http(patched_loop: Any) -> None: cleanup_handler = make_mocked_coro() app.on_cleanup.append(cleanup_handler) - web.run_app(app, print=stopper(patched_loop)) + web.run_app(app, print=stopper(patched_loop), loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, None, 8080, ssl=None, backlog=128, reuse_address=None, reuse_port=None @@ -105,7 +105,7 @@ def test_run_app_http(patched_loop: Any) -> None: def test_run_app_close_loop(patched_loop: Any) -> None: app = web.Application() - web.run_app(app, print=stopper(patched_loop)) + web.run_app(app, print=stopper(patched_loop), loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, None, 8080, ssl=None, backlog=128, reuse_address=None, reuse_port=None @@ -428,7 +428,7 @@ def test_run_app_mixed_bindings( patched_loop: Any, ) -> None: app = web.Application() - web.run_app(app, print=stopper(patched_loop), **run_app_kwargs) + web.run_app(app, print=stopper(patched_loop), **run_app_kwargs, loop=patched_loop) assert patched_loop.create_unix_server.mock_calls == expected_unix_server_calls assert patched_loop.create_server.mock_calls == expected_server_calls @@ -438,7 +438,7 @@ def test_run_app_https(patched_loop: Any) -> None: app = web.Application() ssl_context = ssl.create_default_context() - web.run_app(app, ssl_context=ssl_context, print=stopper(patched_loop)) + web.run_app(app, ssl_context=ssl_context, print=stopper(patched_loop), loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, @@ -458,7 +458,7 @@ def test_run_app_nondefault_host_port( host = "127.0.0.1" app = web.Application() - web.run_app(app, host=host, port=port, print=stopper(patched_loop)) + web.run_app(app, host=host, port=port, print=stopper(patched_loop), loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, host, port, ssl=None, backlog=128, reuse_address=None, reuse_port=None @@ -469,7 +469,7 @@ def test_run_app_multiple_hosts(patched_loop: Any) -> None: hosts = ("127.0.0.1", "127.0.0.2") app = web.Application() - web.run_app(app, host=hosts, print=stopper(patched_loop)) + web.run_app(app, host=hosts, print=stopper(patched_loop), loop=patched_loop) calls = map( lambda h: mock.call( @@ -488,7 +488,7 @@ def test_run_app_multiple_hosts(patched_loop: Any) -> None: def test_run_app_custom_backlog(patched_loop: Any) -> None: app = web.Application() - web.run_app(app, backlog=10, print=stopper(patched_loop)) + web.run_app(app, backlog=10, print=stopper(patched_loop), loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, None, 8080, ssl=None, backlog=10, reuse_address=None, reuse_port=None @@ -497,7 +497,7 @@ def test_run_app_custom_backlog(patched_loop: Any) -> None: def test_run_app_custom_backlog_unix(patched_loop: Any) -> None: app = web.Application() - web.run_app(app, path="/tmp/tmpsock.sock", backlog=10, print=stopper(patched_loop)) + web.run_app(app, path="/tmp/tmpsock.sock", backlog=10, print=stopper(patched_loop), loop=patched_loop) patched_loop.create_unix_server.assert_called_with( mock.ANY, "/tmp/tmpsock.sock", ssl=None, backlog=10 @@ -510,7 +510,7 @@ def test_run_app_http_unix_socket(patched_loop: Any, tmp_path: Any) -> None: sock_path = str(tmp_path / "socket.sock") printer = mock.Mock(wraps=stopper(patched_loop)) - web.run_app(app, path=sock_path, print=printer) + web.run_app(app, path=sock_path, print=printer, loop=patched_loop) patched_loop.create_unix_server.assert_called_with( mock.ANY, sock_path, ssl=None, backlog=128 @@ -525,7 +525,7 @@ def test_run_app_https_unix_socket(patched_loop: Any, tmp_path: Any) -> None: sock_path = str(tmp_path / "socket.sock") ssl_context = ssl.create_default_context() printer = mock.Mock(wraps=stopper(patched_loop)) - web.run_app(app, path=sock_path, ssl_context=ssl_context, print=printer) + web.run_app(app, path=sock_path, ssl_context=ssl_context, print=printer, loop=patched_loop) patched_loop.create_unix_server.assert_called_with( mock.ANY, sock_path, ssl=ssl_context, backlog=128 @@ -539,7 +539,7 @@ def test_run_app_abstract_linux_socket(patched_loop: Any) -> None: sock_path = b"\x00" + uuid4().hex.encode("ascii") app = web.Application() web.run_app( - app, path=sock_path.decode("ascii", "ignore"), print=stopper(patched_loop) + app, path=sock_path.decode("ascii", "ignore"), print=stopper(patched_loop), loop=patched_loop ) patched_loop.create_unix_server.assert_called_with( @@ -556,7 +556,7 @@ def test_run_app_preexisting_inet_socket(patched_loop: Any, mocker: Any) -> None _, port = sock.getsockname() printer = mock.Mock(wraps=stopper(patched_loop)) - web.run_app(app, sock=sock, print=printer) + web.run_app(app, sock=sock, print=printer, loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, sock=sock, backlog=128, ssl=None @@ -574,7 +574,7 @@ def test_run_app_preexisting_inet6_socket(patched_loop: Any) -> None: port = sock.getsockname()[1] printer = mock.Mock(wraps=stopper(patched_loop)) - web.run_app(app, sock=sock, print=printer) + web.run_app(app, sock=sock, print=printer, loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, sock=sock, backlog=128, ssl=None @@ -593,7 +593,7 @@ def test_run_app_preexisting_unix_socket(patched_loop: Any, mocker: Any) -> None os.unlink(sock_path) printer = mock.Mock(wraps=stopper(patched_loop)) - web.run_app(app, sock=sock, print=printer) + web.run_app(app, sock=sock, print=printer, loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, sock=sock, backlog=128, ssl=None @@ -613,7 +613,7 @@ def test_run_app_multiple_preexisting_sockets(patched_loop: Any) -> None: _, port2 = sock2.getsockname() printer = mock.Mock(wraps=stopper(patched_loop)) - web.run_app(app, sock=(sock1, sock2), print=printer) + web.run_app(app, sock=(sock1, sock2), print=printer, loop=patched_loop) patched_loop.create_server.assert_has_calls( [ @@ -671,7 +671,7 @@ def test_startup_cleanup_signals_even_on_failure(patched_loop: Any) -> None: app.on_cleanup.append(cleanup_handler) with pytest.raises(RuntimeError): - web.run_app(app, print=stopper(patched_loop)) + web.run_app(app, print=stopper(patched_loop), loop=patched_loop) startup_handler.assert_called_once_with(app) cleanup_handler.assert_called_once_with(app) @@ -689,7 +689,7 @@ async def make_app(): app.on_cleanup.append(cleanup_handler) return app - web.run_app(make_app(), print=stopper(patched_loop)) + web.run_app(make_app(), print=stopper(patched_loop), loop=patched_loop) patched_loop.create_server.assert_called_with( mock.ANY, None, 8080, ssl=None, backlog=128, reuse_address=None, reuse_port=None @@ -709,7 +709,9 @@ def test_run_app_default_logger(monkeypatch: Any, patched_loop: Any) -> None: mock_logger.configure_mock(**attrs) app = web.Application() - web.run_app(app, debug=True, print=stopper(patched_loop), access_log=mock_logger) + web.run_app( + app, debug=True, print=stopper(patched_loop), access_log=mock_logger, loop=patched_loop + ) mock_logger.setLevel.assert_any_call(logging.DEBUG) mock_logger.hasHandlers.assert_called_with() assert isinstance(mock_logger.addHandler.call_args[0][0], logging.StreamHandler) @@ -726,7 +728,9 @@ def test_run_app_default_logger_setup_requires_debug(patched_loop: Any) -> None: mock_logger.configure_mock(**attrs) app = web.Application() - web.run_app(app, debug=False, print=stopper(patched_loop), access_log=mock_logger) + web.run_app( + app, debug=False, print=stopper(patched_loop), access_log=mock_logger, loop=patched_loop + ) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_not_called() mock_logger.addHandler.assert_not_called() @@ -745,7 +749,7 @@ def test_run_app_default_logger_setup_requires_default_logger( mock_logger.configure_mock(**attrs) app = web.Application() - web.run_app(app, debug=True, print=stopper(patched_loop), access_log=mock_logger) + web.run_app(app, debug=True, print=stopper(patched_loop), access_log=mock_logger, loop=patched_loop) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_not_called() mock_logger.addHandler.assert_not_called() @@ -762,7 +766,7 @@ def test_run_app_default_logger_setup_only_if_unconfigured(patched_loop: Any) -> mock_logger.configure_mock(**attrs) app = web.Application() - web.run_app(app, debug=True, print=stopper(patched_loop), access_log=mock_logger) + web.run_app(app, debug=True, print=stopper(patched_loop), access_log=mock_logger, loop=patched_loop) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_called_with() mock_logger.addHandler.assert_not_called() @@ -779,7 +783,7 @@ async def on_startup(app): app.on_startup.append(on_startup) - web.run_app(app, print=stopper(patched_loop)) + web.run_app(app, print=stopper(patched_loop), loop=patched_loop) assert task.cancelled() @@ -797,7 +801,7 @@ async def on_startup(app): app.on_startup.append(on_startup) - web.run_app(app, print=stopper(patched_loop)) + web.run_app(app, print=stopper(patched_loop), loop=patched_loop) assert task.done() @@ -823,7 +827,7 @@ async def on_startup(app): exc_handler = mock.Mock() patched_loop.set_exception_handler(exc_handler) - web.run_app(app, print=stopper(patched_loop)) + web.run_app(app, print=stopper(patched_loop), loop=patched_loop) assert task.done() msg = { @@ -846,7 +850,7 @@ def base_runner_init_spy(self, *args, **kwargs): app = web.Application() monkeypatch.setattr(BaseRunner, "__init__", base_runner_init_spy) - web.run_app(app, keepalive_timeout=new_timeout, print=stopper(patched_loop)) + web.run_app(app, keepalive_timeout=new_timeout, print=stopper(patched_loop), loop=patched_loop) def test_run_app_context_vars(patched_loop: Any): @@ -877,5 +881,5 @@ async def init(): count += 1 return app - web.run_app(init(), print=stopper(patched_loop)) + web.run_app(init(), print=stopper(patched_loop), loop=patched_loop) assert count == 3 From 5bdc30950fa42f9ffb9f5958167c77eeecd6384b Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 12:34:18 +0100 Subject: [PATCH 11/17] Black --- tests/test_run_app.py | 60 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/tests/test_run_app.py b/tests/test_run_app.py index f04550e726b..60219b2cb95 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -438,7 +438,9 @@ def test_run_app_https(patched_loop: Any) -> None: app = web.Application() ssl_context = ssl.create_default_context() - web.run_app(app, ssl_context=ssl_context, print=stopper(patched_loop), loop=patched_loop) + web.run_app( + app, ssl_context=ssl_context, print=stopper(patched_loop), loop=patched_loop + ) patched_loop.create_server.assert_called_with( mock.ANY, @@ -458,7 +460,9 @@ def test_run_app_nondefault_host_port( host = "127.0.0.1" app = web.Application() - web.run_app(app, host=host, port=port, print=stopper(patched_loop), loop=patched_loop) + web.run_app( + app, host=host, port=port, print=stopper(patched_loop), loop=patched_loop + ) patched_loop.create_server.assert_called_with( mock.ANY, host, port, ssl=None, backlog=128, reuse_address=None, reuse_port=None @@ -497,7 +501,13 @@ def test_run_app_custom_backlog(patched_loop: Any) -> None: def test_run_app_custom_backlog_unix(patched_loop: Any) -> None: app = web.Application() - web.run_app(app, path="/tmp/tmpsock.sock", backlog=10, print=stopper(patched_loop), loop=patched_loop) + web.run_app( + app, + path="/tmp/tmpsock.sock", + backlog=10, + print=stopper(patched_loop), + loop=patched_loop + ) patched_loop.create_unix_server.assert_called_with( mock.ANY, "/tmp/tmpsock.sock", ssl=None, backlog=10 @@ -525,7 +535,9 @@ def test_run_app_https_unix_socket(patched_loop: Any, tmp_path: Any) -> None: sock_path = str(tmp_path / "socket.sock") ssl_context = ssl.create_default_context() printer = mock.Mock(wraps=stopper(patched_loop)) - web.run_app(app, path=sock_path, ssl_context=ssl_context, print=printer, loop=patched_loop) + web.run_app( + app, path=sock_path, ssl_context=ssl_context, print=printer, loop=patched_loop + ) patched_loop.create_unix_server.assert_called_with( mock.ANY, sock_path, ssl=ssl_context, backlog=128 @@ -539,7 +551,10 @@ def test_run_app_abstract_linux_socket(patched_loop: Any) -> None: sock_path = b"\x00" + uuid4().hex.encode("ascii") app = web.Application() web.run_app( - app, path=sock_path.decode("ascii", "ignore"), print=stopper(patched_loop), loop=patched_loop + app, + path=sock_path.decode("ascii", "ignore"), + print=stopper(patched_loop), + loop=patched_loop ) patched_loop.create_unix_server.assert_called_with( @@ -710,7 +725,11 @@ def test_run_app_default_logger(monkeypatch: Any, patched_loop: Any) -> None: app = web.Application() web.run_app( - app, debug=True, print=stopper(patched_loop), access_log=mock_logger, loop=patched_loop + app, + debug=True, + print=stopper(patched_loop), + access_log=mock_logger, + loop=patched_loop ) mock_logger.setLevel.assert_any_call(logging.DEBUG) mock_logger.hasHandlers.assert_called_with() @@ -729,7 +748,11 @@ def test_run_app_default_logger_setup_requires_debug(patched_loop: Any) -> None: app = web.Application() web.run_app( - app, debug=False, print=stopper(patched_loop), access_log=mock_logger, loop=patched_loop + app, + debug=False, + print=stopper(patched_loop), + access_log=mock_logger, + loop=patched_loop ) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_not_called() @@ -749,7 +772,13 @@ def test_run_app_default_logger_setup_requires_default_logger( mock_logger.configure_mock(**attrs) app = web.Application() - web.run_app(app, debug=True, print=stopper(patched_loop), access_log=mock_logger, loop=patched_loop) + web.run_app( + app, + debug=True, + print=stopper(patched_loop), + access_log=mock_logger, + loop=patched_loop + ) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_not_called() mock_logger.addHandler.assert_not_called() @@ -766,7 +795,13 @@ def test_run_app_default_logger_setup_only_if_unconfigured(patched_loop: Any) -> mock_logger.configure_mock(**attrs) app = web.Application() - web.run_app(app, debug=True, print=stopper(patched_loop), access_log=mock_logger, loop=patched_loop) + web.run_app( + app, + debug=True, + print=stopper(patched_loop), + access_log=mock_logger, + loop=patched_loop + ) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_called_with() mock_logger.addHandler.assert_not_called() @@ -850,7 +885,12 @@ def base_runner_init_spy(self, *args, **kwargs): app = web.Application() monkeypatch.setattr(BaseRunner, "__init__", base_runner_init_spy) - web.run_app(app, keepalive_timeout=new_timeout, print=stopper(patched_loop), loop=patched_loop) + web.run_app( + app, + keepalive_timeout=new_timeout, + print=stopper(patched_loop), + loop=patched_loop + ) def test_run_app_context_vars(patched_loop: Any): From a5dd9b8581712fa5345bd0dd618ff2c53cf7c98a Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 12:37:39 +0100 Subject: [PATCH 12/17] Commas --- tests/test_run_app.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_run_app.py b/tests/test_run_app.py index 60219b2cb95..835f2c6714f 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -506,7 +506,7 @@ def test_run_app_custom_backlog_unix(patched_loop: Any) -> None: path="/tmp/tmpsock.sock", backlog=10, print=stopper(patched_loop), - loop=patched_loop + loop=patched_loop, ) patched_loop.create_unix_server.assert_called_with( @@ -554,7 +554,7 @@ def test_run_app_abstract_linux_socket(patched_loop: Any) -> None: app, path=sock_path.decode("ascii", "ignore"), print=stopper(patched_loop), - loop=patched_loop + loop=patched_loop, ) patched_loop.create_unix_server.assert_called_with( @@ -729,7 +729,7 @@ def test_run_app_default_logger(monkeypatch: Any, patched_loop: Any) -> None: debug=True, print=stopper(patched_loop), access_log=mock_logger, - loop=patched_loop + loop=patched_loop, ) mock_logger.setLevel.assert_any_call(logging.DEBUG) mock_logger.hasHandlers.assert_called_with() @@ -752,7 +752,7 @@ def test_run_app_default_logger_setup_requires_debug(patched_loop: Any) -> None: debug=False, print=stopper(patched_loop), access_log=mock_logger, - loop=patched_loop + loop=patched_loop, ) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_not_called() @@ -777,7 +777,7 @@ def test_run_app_default_logger_setup_requires_default_logger( debug=True, print=stopper(patched_loop), access_log=mock_logger, - loop=patched_loop + loop=patched_loop, ) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_not_called() @@ -800,7 +800,7 @@ def test_run_app_default_logger_setup_only_if_unconfigured(patched_loop: Any) -> debug=True, print=stopper(patched_loop), access_log=mock_logger, - loop=patched_loop + loop=patched_loop, ) mock_logger.setLevel.assert_not_called() mock_logger.hasHandlers.assert_called_with() @@ -889,7 +889,7 @@ def base_runner_init_spy(self, *args, **kwargs): app, keepalive_timeout=new_timeout, print=stopper(patched_loop), - loop=patched_loop + loop=patched_loop, ) From 0621e30020f9dae243d8ee178b7622a26a7623b9 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 13:02:34 +0100 Subject: [PATCH 13/17] Update and rename 5572.bugfix to 5572.feature --- CHANGES/5572.bugfix | 1 - CHANGES/5572.feature | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 CHANGES/5572.bugfix create mode 100644 CHANGES/5572.feature diff --git a/CHANGES/5572.bugfix b/CHANGES/5572.bugfix deleted file mode 100644 index 9c3bcdc8980..00000000000 --- a/CHANGES/5572.bugfix +++ /dev/null @@ -1 +0,0 @@ -Always create a new event loop in ``aiohttp.web.run_app()`` to avoid an error caused by using ``asyncio.run()`` beforehand. diff --git a/CHANGES/5572.feature b/CHANGES/5572.feature new file mode 100644 index 00000000000..a5d60fb6ee3 --- /dev/null +++ b/CHANGES/5572.feature @@ -0,0 +1,2 @@ +Always create a new event loop in ``aiohttp.web.run_app()``. +This adds better compatibility with ``asyncio.run()`` or if trying to run multiple apps in sequence. From add49b058b9958a5dcda138c77f605c5552920ec Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 13:04:49 +0100 Subject: [PATCH 14/17] Update tests/test_web_runner.py Co-authored-by: Sviatoslav Sydorenko --- tests/test_web_runner.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index 865dc35a2bc..a9c8a153447 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -217,7 +217,12 @@ def test_run_after_asyncio_run() -> None: async def nothing(): pass + def spy(cls): + spy.called = True + spy.called = False + async def shutdown(): + spy() raise web.GracefulExit() # asyncio.run() creates a new loop and closes it. From 84f2a13677af8dcdf108358859df2d8d2dd2ecb5 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 13:06:00 +0100 Subject: [PATCH 15/17] Update test_web_runner.py --- tests/test_web_runner.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index a9c8a153447..baf82670b63 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -231,7 +231,6 @@ async def shutdown(): app = web.Application() # create_task() will delay the function until app is run. app.on_startup.append(lambda a: asyncio.create_task(shutdown())) - try: - web.run_app(app) - except RuntimeError: - pytest.fail("run_app() should work after asyncio.run().") + + web.run_app(app) + assert spy.called, "run_app() should work after asyncio.run()." From c9f15364d537cdf2074344d54ae28e52033aceb5 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 13:09:39 +0100 Subject: [PATCH 16/17] Update test_web_runner.py --- tests/test_web_runner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index baf82670b63..b7769d4f004 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -219,6 +219,7 @@ async def nothing(): def spy(cls): spy.called = True + spy.called = False async def shutdown(): From cdf183526690d95c0c0469712d77f62691599828 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 19 Jun 2021 13:15:14 +0100 Subject: [PATCH 17/17] Update test_web_runner.py --- tests/test_web_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_web_runner.py b/tests/test_web_runner.py index b7769d4f004..48ec3944337 100644 --- a/tests/test_web_runner.py +++ b/tests/test_web_runner.py @@ -217,7 +217,7 @@ def test_run_after_asyncio_run() -> None: async def nothing(): pass - def spy(cls): + def spy(): spy.called = True spy.called = False