diff --git a/aiohttp/web.py b/aiohttp/web.py index 8d31568cfef..1e3115b1f7f 100644 --- a/aiohttp/web.py +++ b/aiohttp/web.py @@ -1,8 +1,6 @@ import asyncio -import os import signal import socket -import stat import sys import warnings from argparse import ArgumentParser @@ -376,15 +374,6 @@ def _make_server_creators(handler, *, loop, ssl_context, ) uris.append('{}://unix:{}:'.format(scheme, path)) - # Clean up prior socket path if stale and not abstract. - # CPython 3.5.3+'s event loop already does this. See - # https://github.com/python/asyncio/issues/425 - if path[0] not in (0, '\x00'): # pragma: no branch - try: - if stat.S_ISSOCK(os.stat(path).st_mode): - os.remove(path) - except FileNotFoundError: - pass for sock in socks: server_creations.append( loop.create_server( @@ -403,11 +392,9 @@ def _make_server_creators(handler, *, loop, ssl_context, def run_app(app, *, host=None, port=None, path=None, sock=None, shutdown_timeout=60.0, ssl_context=None, print=print, backlog=128, access_log_format=None, - access_log=access_logger, handle_signals=True, loop=None): + access_log=access_logger, handle_signals=True): """Run an app locally""" - user_supplied_loop = loop is not None - if loop is None: - loop = asyncio.get_event_loop() + loop = asyncio.get_event_loop() app._set_loop(loop) app.freeze() @@ -455,10 +442,9 @@ def run_app(app, *, host=None, port=None, path=None, sock=None, loop.run_until_complete(handler.shutdown(shutdown_timeout)) finally: loop.run_until_complete(app.cleanup()) - if not user_supplied_loop: - if hasattr(loop, 'shutdown_asyncgens'): - loop.run_until_complete(loop.shutdown_asyncgens()) - loop.close() + if hasattr(loop, 'shutdown_asyncgens'): + loop.run_until_complete(loop.shutdown_asyncgens()) + loop.close() def main(argv): diff --git a/docs/web_reference.rst b/docs/web_reference.rst index 6de5ac5aa05..175f4b9b73e 100644 --- a/docs/web_reference.rst +++ b/docs/web_reference.rst @@ -2246,7 +2246,7 @@ Utilities ssl_context=None, print=print, backlog=128, \ access_log_format=None, \ access_log=aiohttp.log.access_logger, \ - handle_signals=True, loop=None) + handle_signals=True) A utility function for running an application, serving it until keyboard interrupt and performing a @@ -2256,8 +2256,6 @@ Utilities Perhaps production config will use more sophisticated runner but it good enough at least at very beginning stage. - The function uses *app.loop* as event loop to run. - The server will listen on any host or Unix domain socket path you supply. If no hosts or paths are supplied, or only a port is supplied, a TCP server listening on 0.0.0.0 (all hosts) will be launched. @@ -2317,13 +2315,6 @@ Utilities :param bool handle_signals: override signal TERM handling to gracefully exit the application. - :param loop: an *event loop* used for running the application - (``None`` by default). - - If the loop is not explicitly specified the function - closes it by :meth:`~asyncio.AbstractEventLoop.close` call but - **does nothing** for **non-default** loop. - Constants --------- diff --git a/tests/test_run_app.py b/tests/test_run_app.py index 44e90c63783..cb5c21bcfac 100644 --- a/tests/test_run_app.py +++ b/tests/test_run_app.py @@ -72,15 +72,15 @@ def f(*args): def test_run_app_http(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') app = web.Application() mocker.spy(app, 'startup') - web.run_app(app, loop=loop, print=stopper(loop)) + web.run_app(app, print=stopper(loop)) - assert not loop.is_closed() loop.create_server.assert_called_with(mock.ANY, '0.0.0.0', 8080, ssl=None, backlog=128) app.startup.assert_called_once_with() @@ -88,6 +88,7 @@ def test_run_app_http(loop, mocker): def test_run_app_close_loop(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') @@ -98,7 +99,6 @@ def test_run_app_close_loop(loop, mocker): web.run_app(app, print=stopper(loop)) - assert loop.is_closed() loop.create_server.assert_called_with(mock.ANY, '0.0.0.0', 8080, ssl=None, backlog=128) app.startup.assert_called_once_with() @@ -221,10 +221,11 @@ def test_run_app_close_loop(loop, mocker): def test_run_app_mixed_bindings(mocker, run_app_kwargs, expected_server_calls, expected_unix_server_calls): app = mocker.MagicMock() - loop = mocker.MagicMock() + loop = mocker.MagicMock(spec=asyncio.AbstractEventLoop) + asyncio.set_event_loop(loop) mocker.patch('asyncio.gather') - web.run_app(app, loop=loop, print=None, **run_app_kwargs) + web.run_app(app, print=None, **run_app_kwargs) assert loop.create_unix_server.mock_calls == expected_unix_server_calls assert loop.create_server.mock_calls == expected_server_calls @@ -233,15 +234,14 @@ def test_run_app_mixed_bindings(mocker, run_app_kwargs, expected_server_calls, def test_run_app_http_access_format(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') app = web.Application() mocker.spy(app, 'startup') - web.run_app(app, loop=loop, - print=stopper(loop), access_log_format='%a') + web.run_app(app, print=stopper(loop), access_log_format='%a') - assert not loop.is_closed() cs = loop.create_server cs.assert_called_with(mock.ANY, '0.0.0.0', 8080, ssl=None, backlog=128) assert cs.call_args[0][0]._kwargs['access_log_format'] == '%a' @@ -251,6 +251,7 @@ def test_run_app_http_access_format(loop, mocker): def test_run_app_https(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') app = web.Application() @@ -258,10 +259,8 @@ def test_run_app_https(loop, mocker): ssl_context = ssl.create_default_context() - web.run_app(app, loop=loop, - ssl_context=ssl_context, print=stopper(loop)) + web.run_app(app, ssl_context=ssl_context, print=stopper(loop)) - assert not loop.is_closed() loop.create_server.assert_called_with(mock.ANY, '0.0.0.0', 8443, ssl=ssl_context, backlog=128) app.startup.assert_called_once_with() @@ -269,6 +268,7 @@ def test_run_app_https(loop, mocker): def test_run_app_nondefault_host_port(loop, unused_port, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) port = unused_port() host = '127.0.0.1' @@ -278,10 +278,8 @@ def test_run_app_nondefault_host_port(loop, unused_port, mocker): app = web.Application() mocker.spy(app, 'startup') - web.run_app(app, loop=loop, - host=host, port=port, print=stopper(loop)) + web.run_app(app, host=host, port=port, print=stopper(loop)) - assert not loop.is_closed() loop.create_server.assert_called_with(mock.ANY, host, port, ssl=None, backlog=128) app.startup.assert_called_once_with() @@ -289,15 +287,15 @@ def test_run_app_nondefault_host_port(loop, unused_port, mocker): def test_run_app_custom_backlog(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') app = web.Application() mocker.spy(app, 'startup') - web.run_app(app, loop=loop, backlog=10, print=stopper(loop)) + web.run_app(app, backlog=10, print=stopper(loop)) - assert not loop.is_closed() loop.create_server.assert_called_with(mock.ANY, '0.0.0.0', 8080, ssl=None, backlog=10) app.startup.assert_called_once_with() @@ -306,6 +304,7 @@ def test_run_app_custom_backlog(loop, mocker): @skip_if_no_unix_socks def test_run_app_http_unix_socket(loop, mocker, shorttmpdir): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_unix_server') @@ -314,10 +313,8 @@ def test_run_app_http_unix_socket(loop, mocker, shorttmpdir): sock_path = str(shorttmpdir.join('socket.sock')) printer = mock.Mock(wraps=stopper(loop)) - web.run_app(app, loop=loop, path=sock_path, - print=printer) + web.run_app(app, path=sock_path, print=printer) - assert not loop.is_closed() loop.create_unix_server.assert_called_with(mock.ANY, sock_path, ssl=None, backlog=128) app.startup.assert_called_once_with() @@ -327,6 +324,7 @@ def test_run_app_http_unix_socket(loop, mocker, shorttmpdir): @skip_if_no_unix_socks def test_run_app_https_unix_socket(loop, mocker, shorttmpdir): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_unix_server') @@ -336,69 +334,27 @@ def test_run_app_https_unix_socket(loop, mocker, shorttmpdir): sock_path = str(shorttmpdir.join('socket.sock')) ssl_context = ssl.create_default_context() printer = mock.Mock(wraps=stopper(loop)) - web.run_app(app, loop=loop, path=sock_path, ssl_context=ssl_context, - print=printer) + web.run_app(app, path=sock_path, ssl_context=ssl_context, print=printer) - assert not loop.is_closed() loop.create_unix_server.assert_called_with(mock.ANY, sock_path, ssl=ssl_context, backlog=128) app.startup.assert_called_once_with() assert "https://unix:{}:".format(sock_path) in printer.call_args[0][0] -@skip_if_no_unix_socks -def test_run_app_stale_unix_socket(loop, mocker, shorttmpdir): - """Older asyncio event loop implementations are known to halt server - creation when a socket path from a previous server bind still exists. - """ - skip_if_no_dict(loop) - - app = web.Application() - - sock_path = shorttmpdir.join('socket.sock') - sock_path_string = str(sock_path) - - web.run_app(app, loop=loop, - path=sock_path_string, print=stopper(loop)) - assert not loop.is_closed() - - if sock_path.check(): - # New app run using same socket path - with loop_context() as loop: - mocker.spy(loop, 'create_unix_server') - - app = web.Application() - - mocker.spy(app, 'startup') - mocker.spy(os, 'remove') - printer = mock.Mock(wraps=stopper(loop)) - - web.run_app(app, loop=loop, - path=sock_path_string, print=printer) - os.remove.assert_called_with(sock_path_string) - loop.create_unix_server.assert_called_with( - mock.ANY, - sock_path_string, - ssl=None, - backlog=128 - ) - app.startup.assert_called_once_with() - assert ("http://unix:{}:".format(sock_path) - in printer.call_args[0][0]) - - @skip_if_no_unix_socks @skip_if_no_abstract_paths def test_run_app_abstract_linux_socket(loop, mocker): sock_path = b"\x00" + uuid4().hex.encode('ascii') + asyncio.set_event_loop(loop) app = web.Application() web.run_app( - app, path=sock_path.decode('ascii', 'ignore'), loop=loop, - print=stopper(loop)) + app, path=sock_path.decode('ascii', 'ignore'), print=stopper(loop)) # New app run using same socket path with loop_context() as loop: + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_unix_server') app = web.Application() @@ -406,7 +362,7 @@ def test_run_app_abstract_linux_socket(loop, mocker): mocker.spy(app, 'startup') mocker.spy(os, 'remove') - web.run_app(app, path=sock_path, print=stopper(loop), loop=loop) + web.run_app(app, path=sock_path, print=stopper(loop)) # Abstract paths don't exist on the file system, so no attempt should # be made to remove. @@ -423,6 +379,7 @@ def test_run_app_abstract_linux_socket(loop, mocker): @skip_if_no_unix_socks def test_run_app_existing_file_conflict(loop, mocker, shorttmpdir): + asyncio.set_event_loop(loop) app = web.Application() sock_path = shorttmpdir.join('socket.sock') sock_path.ensure() @@ -430,8 +387,7 @@ def test_run_app_existing_file_conflict(loop, mocker, shorttmpdir): mocker.spy(os, 'remove') with pytest.raises(OSError): - web.run_app(app, loop=loop, - path=sock_path_str, print=mock.Mock()) + web.run_app(app, path=sock_path_str, print=mock.Mock()) # No attempt should be made to remove a non-socket file assert mock.call([sock_path_str]) not in os.remove.mock_calls @@ -439,6 +395,7 @@ def test_run_app_existing_file_conflict(loop, mocker, shorttmpdir): def test_run_app_preexisting_inet_socket(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') @@ -451,9 +408,8 @@ def test_run_app_preexisting_inet_socket(loop, mocker): _, port = sock.getsockname() printer = mock.Mock(wraps=stopper(loop)) - web.run_app(app, loop=loop, sock=sock, print=printer) + web.run_app(app, sock=sock, print=printer) - assert not loop.is_closed() loop.create_server.assert_called_with( mock.ANY, sock=sock, backlog=128, ssl=None ) @@ -464,6 +420,7 @@ def test_run_app_preexisting_inet_socket(loop, mocker): @pytest.mark.skipif(not HAS_IPV6, reason="IPv6 is not available") def test_run_app_preexisting_inet6_socket(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') @@ -476,9 +433,8 @@ def test_run_app_preexisting_inet6_socket(loop, mocker): port = sock.getsockname()[1] printer = mock.Mock(wraps=stopper(loop)) - web.run_app(app, loop=loop, sock=sock, print=printer) + web.run_app(app, sock=sock, print=printer) - assert not loop.is_closed() loop.create_server.assert_called_with( mock.ANY, sock=sock, backlog=128, ssl=None ) @@ -489,6 +445,7 @@ def test_run_app_preexisting_inet6_socket(loop, mocker): @skip_if_no_unix_socks def test_run_app_preexisting_unix_socket(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') @@ -502,9 +459,8 @@ def test_run_app_preexisting_unix_socket(loop, mocker): os.unlink(sock_path) printer = mock.Mock(wraps=stopper(loop)) - web.run_app(app, loop=loop, sock=sock, print=printer) + web.run_app(app, sock=sock, print=printer) - assert not loop.is_closed() loop.create_server.assert_called_with( mock.ANY, sock=sock, backlog=128, ssl=None ) @@ -514,6 +470,7 @@ def test_run_app_preexisting_unix_socket(loop, mocker): def test_run_app_multiple_preexisting_sockets(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') @@ -529,7 +486,7 @@ def test_run_app_multiple_preexisting_sockets(loop, mocker): _, port2 = sock2.getsockname() printer = mock.Mock(wraps=stopper(loop)) - web.run_app(app, loop=loop, sock=(sock1, sock2), print=printer) + web.run_app(app, sock=(sock1, sock2), print=printer) loop.create_server.assert_has_calls([ mock.call(mock.ANY, sock=sock1, backlog=128, ssl=None), @@ -574,6 +531,7 @@ def test_sigterm(loop, mocker): def test_startup_cleanup_signals(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') @@ -581,7 +539,7 @@ def test_startup_cleanup_signals(loop, mocker): mocker.spy(app, 'startup') mocker.spy(app, 'cleanup') - web.run_app(app, loop=loop, host=(), print=stopper(loop)) + web.run_app(app, host=(), print=stopper(loop)) app.startup.assert_called_once_with() app.cleanup.assert_called_once_with() @@ -589,6 +547,7 @@ def test_startup_cleanup_signals(loop, mocker): def test_startup_cleanup_signals_even_on_failure(loop, mocker): skip_if_no_dict(loop) + asyncio.set_event_loop(loop) setattr(loop, 'create_server', mock.Mock(side_effect=RuntimeError())) @@ -597,7 +556,7 @@ def test_startup_cleanup_signals_even_on_failure(loop, mocker): mocker.spy(app, 'cleanup') with pytest.raises(RuntimeError): - web.run_app(app, loop=loop, print=stopper(loop)) + web.run_app(app, print=stopper(loop)) app.startup.assert_called_once_with() app.cleanup.assert_called_once_with()