diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst
index d99d30a2..4ce4d4c7 100644
--- a/docs/versionhistory.rst
+++ b/docs/versionhistory.rst
@@ -42,6 +42,7 @@ This library adheres to `Semantic Versioning 2.0 `_.
arrives in an exception group)
- Fixed support for Linux abstract namespaces in UNIX sockets that was broken in v4.2
(#781 _; PR by @tapetersen)
+- Fixed ``KeyboardInterrupt`` (ctrl+c) hanging the asyncio pytest runner
**4.4.0**
diff --git a/src/anyio/_backends/_asyncio.py b/src/anyio/_backends/_asyncio.py
index 97edb8ad..0d4cdf65 100644
--- a/src/anyio/_backends/_asyncio.py
+++ b/src/anyio/_backends/_asyncio.py
@@ -2082,13 +2082,23 @@ async def _run_tests_and_fixtures(
tuple[Awaitable[T_Retval], asyncio.Future[T_Retval]]
],
) -> None:
+ from _pytest.outcomes import OutcomeException
+
with receive_stream, self._send_stream:
async for coro, future in receive_stream:
try:
retval = await coro
+ except CancelledError as exc:
+ if not future.cancelled():
+ future.cancel(*exc.args)
+
+ raise
except BaseException as exc:
if not future.cancelled():
future.set_exception(exc)
+
+ if not isinstance(exc, (Exception, OutcomeException)):
+ raise
else:
if not future.cancelled():
future.set_result(retval)
diff --git a/tests/test_pytest_plugin.py b/tests/test_pytest_plugin.py
index 2b7d9ca4..1aa8911e 100644
--- a/tests/test_pytest_plugin.py
+++ b/tests/test_pytest_plugin.py
@@ -441,3 +441,30 @@ async def test_debugger_exit():
result = testdir.runpytest(*pytest_args)
result.assert_outcomes()
+
+
+@pytest.mark.parametrize("anyio_backend", get_all_backends(), indirect=True)
+def test_keyboardinterrupt_during_test(
+ testdir: Pytester, anyio_backend_name: str
+) -> None:
+ testdir.makepyfile(
+ f"""
+ import pytest
+ from anyio import create_task_group, sleep
+
+ @pytest.fixture
+ def anyio_backend():
+ return {anyio_backend_name!r}
+
+ async def send_keyboardinterrupt():
+ raise KeyboardInterrupt
+
+ @pytest.mark.anyio
+ async def test_anyio_mark_first():
+ async with create_task_group() as tg:
+ tg.start_soon(send_keyboardinterrupt)
+ await sleep(10)
+ """
+ )
+
+ testdir.runpytest_subprocess(*pytest_args, timeout=3)