From dc361c45b621dec2a26f3f8c1b918a84c0beff08 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Wed, 14 Jul 2021 12:17:54 +0800 Subject: [PATCH 1/9] Switch to ThreadedChildWatcher and test --- aiohttp/test_utils.py | 2 +- tests/test_loop.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index 06155bae19f..36a860b0171 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -497,7 +497,7 @@ def setup_test_loop( # * https://stackoverflow.com/a/58614689/595220 # * https://bugs.python.org/issue35621 # * https://github.com/python/cpython/pull/14344 - watcher = asyncio.MultiLoopChildWatcher() + watcher = asyncio.ThreadedChildWatcher() except AttributeError: # Python < 3.8 watcher = asyncio.SafeChildWatcher() watcher.attach_loop(loop) diff --git a/tests/test_loop.py b/tests/test_loop.py index 50fe5a8ad69..0a14ab31568 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -6,7 +6,7 @@ import pytest from aiohttp import web -from aiohttp.test_utils import AioHTTPTestCase +from aiohttp.test_utils import AioHTTPTestCase, setup_test_loop @pytest.mark.skipif( @@ -43,3 +43,10 @@ def test_default_loop(self) -> None: def test_default_loop(loop: Any) -> None: assert asyncio.get_event_loop() is loop + + +def test_setup_loop_non_main_thread(): + # Ensures setup_test_loop can be called by pytest-xdist in non-main thread. + t = threading.Thread(target=setup_test_loop) + t.start() + t.join() From 95b0e5dd768643837da8d9d020b7a6fcfa838cca Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Wed, 14 Jul 2021 12:25:34 +0800 Subject: [PATCH 2/9] Add to changes --- CHANGES/5877.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGES/5877.bugfix diff --git a/CHANGES/5877.bugfix b/CHANGES/5877.bugfix new file mode 100644 index 00000000000..3e22d8ba04d --- /dev/null +++ b/CHANGES/5877.bugfix @@ -0,0 +1 @@ +Uses `ThreadedChildWatcher` under POSIX to allow setting up test loop in non-main thread. From 6b2d5af15433962e0c4aa23f99fa3ab3fd1ca3d4 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Wed, 14 Jul 2021 12:28:09 +0800 Subject: [PATCH 3/9] Fix mypy --- tests/test_loop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_loop.py b/tests/test_loop.py index 0a14ab31568..3cd032e7b1c 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -45,7 +45,7 @@ def test_default_loop(loop: Any) -> None: assert asyncio.get_event_loop() is loop -def test_setup_loop_non_main_thread(): +def test_setup_loop_non_main_thread() -> None: # Ensures setup_test_loop can be called by pytest-xdist in non-main thread. t = threading.Thread(target=setup_test_loop) t.start() From 3072ae0ec6b786fc7f9f19484d622c1161fb711b Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Wed, 14 Jul 2021 12:57:22 +0800 Subject: [PATCH 4/9] Tear down test loop in thread --- tests/test_loop.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_loop.py b/tests/test_loop.py index 3cd032e7b1c..b5cc6f9f612 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -6,7 +6,7 @@ import pytest from aiohttp import web -from aiohttp.test_utils import AioHTTPTestCase, setup_test_loop +from aiohttp.test_utils import AioHTTPTestCase, loop_context @pytest.mark.skipif( @@ -46,7 +46,11 @@ def test_default_loop(loop: Any) -> None: def test_setup_loop_non_main_thread() -> None: + def target(): + with loop_context(): + pass + # Ensures setup_test_loop can be called by pytest-xdist in non-main thread. - t = threading.Thread(target=setup_test_loop) + t = threading.Thread(target=target) t.start() t.join() From 8af64ab723ff960d3c5d68973a20f08807150664 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Wed, 14 Jul 2021 13:00:03 +0800 Subject: [PATCH 5/9] Fix mypy --- tests/test_loop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_loop.py b/tests/test_loop.py index b5cc6f9f612..9e2e734131b 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -46,7 +46,7 @@ def test_default_loop(loop: Any) -> None: def test_setup_loop_non_main_thread() -> None: - def target(): + def target() -> None: with loop_context(): pass From 3b51df0426fbeeb59e5fed207c545ec331622aa4 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Tue, 27 Jul 2021 10:29:03 +0800 Subject: [PATCH 6/9] Fix changelog --- CHANGES/5877.bugfix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES/5877.bugfix b/CHANGES/5877.bugfix index 3e22d8ba04d..5a8108a9a45 100644 --- a/CHANGES/5877.bugfix +++ b/CHANGES/5877.bugfix @@ -1 +1 @@ -Uses `ThreadedChildWatcher` under POSIX to allow setting up test loop in non-main thread. +Uses :py:class:`~asyncio.ThreadedChildWatcher` under POSIX to allow setting up test loop in non-main thread. From 0d624ca1d5a8da7e78e89a63ba7094db3290d312 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Tue, 27 Jul 2021 10:39:07 +0800 Subject: [PATCH 7/9] Create subprocess in test loop --- tests/test_loop.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_loop.py b/tests/test_loop.py index 9e2e734131b..7028eba600a 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -13,7 +13,6 @@ platform.system() == "Windows", reason="the test is not valid for Windows" ) async def test_subprocess_co(loop: Any) -> None: - assert threading.current_thread() is threading.main_thread() proc = await asyncio.create_subprocess_shell( "exit 0", stdin=asyncio.subprocess.DEVNULL, @@ -47,8 +46,9 @@ def test_default_loop(loop: Any) -> None: def test_setup_loop_non_main_thread() -> None: def target() -> None: - with loop_context(): - pass + with loop_context() as loop: + assert asyncio.get_event_loop() is loop + loop.run_until_complete(test_subprocess_co(loop)) # Ensures setup_test_loop can be called by pytest-xdist in non-main thread. t = threading.Thread(target=target) From b56b1397c494c2a23a1c95ef4470a6bf2a352600 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Tue, 27 Jul 2021 11:58:42 +0800 Subject: [PATCH 8/9] Fail test on python 3.7 --- tests/test_loop.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/test_loop.py b/tests/test_loop.py index 7028eba600a..4fd330cc203 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -6,6 +6,7 @@ import pytest from aiohttp import web +from aiohttp.helpers import PY_38 from aiohttp.test_utils import AioHTTPTestCase, loop_context @@ -44,13 +45,22 @@ def test_default_loop(loop: Any) -> None: assert asyncio.get_event_loop() is loop +@pytest.mark.xfail(not PY_38, reason="ThreadedChildWatcher is only available in 3.8+") def test_setup_loop_non_main_thread() -> None: + child_exc = None + def target() -> None: - with loop_context() as loop: - assert asyncio.get_event_loop() is loop - loop.run_until_complete(test_subprocess_co(loop)) + try: + with loop_context() as loop: + assert asyncio.get_event_loop() is loop + loop.run_until_complete(test_subprocess_co(loop)) + except Exception as exc: + nonlocal child_exc + child_exc = exc # Ensures setup_test_loop can be called by pytest-xdist in non-main thread. t = threading.Thread(target=target) t.start() t.join() + + assert child_exc is None From b99f35cd7cba5e08bd92a3736f4ff06e73a605da Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Wed, 28 Jul 2021 09:34:15 +0800 Subject: [PATCH 9/9] Update tests/test_loop.py Co-authored-by: Sviatoslav Sydorenko --- tests/test_loop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_loop.py b/tests/test_loop.py index 4fd330cc203..b72771a175a 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -14,6 +14,7 @@ platform.system() == "Windows", reason="the test is not valid for Windows" ) async def test_subprocess_co(loop: Any) -> None: + assert PY_38 or threading.current_thread() is threading.main_thread() proc = await asyncio.create_subprocess_shell( "exit 0", stdin=asyncio.subprocess.DEVNULL,