From c626323572ca92be0145dcce2c44c413d7936c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Sat, 9 Dec 2023 13:07:23 +0200 Subject: [PATCH] Fixed asyncio backend options not being used Fixes #643. --- docs/versionhistory.rst | 2 ++ src/anyio/_backends/_asyncio.py | 14 +++++++++----- tests/test_eventloop.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index af625fd8..78ef5105 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -11,6 +11,8 @@ This library adheres to `Semantic Versioning 2.0 `_. - Fixed adjusting the total number of tokens in a ``CapacityLimiter`` on asyncio failing to wake up tasks waiting to acquire the limiter in certain edge cases (fixed with help from Egor Blagov) +- Fixed ``loop_factory`` and ``use_uvloop`` options not being used on the asyncio + backend (`#643 `_) **4.1.0** diff --git a/src/anyio/_backends/_asyncio.py b/src/anyio/_backends/_asyncio.py index 7a10e17b..bb6a9bff 100644 --- a/src/anyio/_backends/_asyncio.py +++ b/src/anyio/_backends/_asyncio.py @@ -16,7 +16,6 @@ get_running_loop, sleep, ) -from asyncio import run as native_run from asyncio.base_events import _run_until_complete_cb # type: ignore[attr-defined] from collections import OrderedDict, deque from collections.abc import AsyncIterator, Generator, Iterable @@ -165,7 +164,7 @@ def run(self, coro: Coroutine[T_Retval], *, context=None) -> T_Retval: if context is None: context = self._context - task = self._loop.create_task(coro, context=context) + task = context.run(self._loop.create_task, coro) if ( threading.current_thread() is threading.main_thread() @@ -1950,9 +1949,14 @@ async def wrapper() -> T_Retval: del _task_states[task] debug = options.get("debug", False) - options.get("loop_factory", None) - options.get("use_uvloop", False) - return native_run(wrapper(), debug=debug) + loop_factory = options.get("loop_factory", None) + if loop_factory is None and options.get("use_uvloop", False): + import uvloop + + loop_factory = uvloop.new_event_loop + + with Runner(debug=debug, loop_factory=loop_factory) as runner: + return runner.run(wrapper()) @classmethod def current_token(cls) -> object: diff --git a/tests/test_eventloop.py b/tests/test_eventloop.py index c0c8ab5f..73ad0c29 100644 --- a/tests/test_eventloop.py +++ b/tests/test_eventloop.py @@ -2,6 +2,7 @@ import asyncio import math +from asyncio import get_running_loop from unittest.mock import AsyncMock import pytest @@ -44,3 +45,32 @@ async def async_add(x: int, y: int) -> int: result = run(asyncio.create_task, async_add(1, 2), backend="asyncio") assert result == 3 + + +class TestAsyncioOptions: + def test_debug(self) -> None: + async def main() -> bool: + return get_running_loop().get_debug() + + debug = run(main, backend="asyncio", backend_options={"debug": True}) + assert debug is True + + def test_loop_factory(self) -> None: + async def main() -> type: + return type(get_running_loop()) + + uvloop = pytest.importorskip("uvloop", reason="uvloop not installed") + loop_class = run( + main, + backend="asyncio", + backend_options={"loop_factory": uvloop.new_event_loop}, + ) + assert issubclass(loop_class, uvloop.Loop) + + def test_use_uvloop(self) -> None: + async def main() -> type: + return type(get_running_loop()) + + uvloop = pytest.importorskip("uvloop", reason="uvloop not installed") + loop_class = run(main, backend="asyncio", backend_options={"use_uvloop": True}) + assert issubclass(loop_class, uvloop.Loop)