From 07c2df74f98fb5c5f7774a00699b3dff1c7076bb Mon Sep 17 00:00:00 2001 From: Tobias Petersen Date: Mon, 21 Nov 2022 13:36:05 +0100 Subject: [PATCH 1/4] Add asyncio detection based on get_running_loop --- sniffio/_impl.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sniffio/_impl.py b/sniffio/_impl.py index c1a7bbf..61dd19e 100644 --- a/sniffio/_impl.py +++ b/sniffio/_impl.py @@ -74,6 +74,12 @@ async def generic_sleep(seconds): # Need to sniff for asyncio if "asyncio" in sys.modules: import asyncio + try: + asyncio.get_running_loop() + return "asyncio" + except (AttributeError, RuntimeError): + pass + try: current_task = asyncio.current_task # type: ignore[attr-defined] except AttributeError: From cd98c11e12d6038158dd94ab42e1aa0ace11ccbf Mon Sep 17 00:00:00 2001 From: Tobias Petersen Date: Mon, 21 Nov 2022 13:47:51 +0100 Subject: [PATCH 2/4] Add test for detection in sync callback --- sniffio/_tests/test_sniffio.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/sniffio/_tests/test_sniffio.py b/sniffio/_tests/test_sniffio.py index 984c8c0..e0d284f 100644 --- a/sniffio/_tests/test_sniffio.py +++ b/sniffio/_tests/test_sniffio.py @@ -58,6 +58,30 @@ async def this_is_asyncio(): current_async_library() +def test_in_call_soon_threadsafe(): + import asyncio + + asynclib = None + completed = asyncio.Event() + + def sync_in_loop(): + nonlocal asynclib + try: + asynclib = current_async_library() + finally: + completed.set() + + async def async_in_loop(): + await completed.wait() + + loop = asyncio.new_event_loop() + handle = loop.call_soon_threadsafe(sync_in_loop) + loop.run_until_complete(async_in_loop()) + loop.close() + + assert asynclib == 'asyncio' + + # https://github.com/dabeaz/curio/pull/354 @pytest.mark.skipif( os.name == "nt" and sys.version_info >= (3, 9), From e72c98c24a6e183081eb36ce642381f9b7ec0cb3 Mon Sep 17 00:00:00 2001 From: Tobias Petersen Date: Mon, 21 Nov 2022 14:22:47 +0100 Subject: [PATCH 3/4] Refactored test_in_call_soon to work on python 3.7, 3.8 --- sniffio/_impl.py | 4 +++- sniffio/_tests/test_sniffio.py | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sniffio/_impl.py b/sniffio/_impl.py index 61dd19e..07fa1fb 100644 --- a/sniffio/_impl.py +++ b/sniffio/_impl.py @@ -77,7 +77,9 @@ async def generic_sleep(seconds): try: asyncio.get_running_loop() return "asyncio" - except (AttributeError, RuntimeError): + except AttributeError: + pass + except RuntimeError: pass try: diff --git a/sniffio/_tests/test_sniffio.py b/sniffio/_tests/test_sniffio.py index e0d284f..1b3babe 100644 --- a/sniffio/_tests/test_sniffio.py +++ b/sniffio/_tests/test_sniffio.py @@ -62,9 +62,8 @@ def test_in_call_soon_threadsafe(): import asyncio asynclib = None - completed = asyncio.Event() - def sync_in_loop(): + def sync_in_loop(completed): nonlocal asynclib try: asynclib = current_async_library() @@ -72,10 +71,11 @@ def sync_in_loop(): completed.set() async def async_in_loop(): + completed = asyncio.Event() + loop.call_soon_threadsafe(sync_in_loop, completed) await completed.wait() loop = asyncio.new_event_loop() - handle = loop.call_soon_threadsafe(sync_in_loop) loop.run_until_complete(async_in_loop()) loop.close() From ec7d2503681a2fa8c5d627437da0d4ea16488489 Mon Sep 17 00:00:00 2001 From: Tobias Petersen Date: Mon, 21 Nov 2022 14:51:25 +0100 Subject: [PATCH 4/4] Refactored detect_loop to only fall back on current_task if get_running_loop fails --- sniffio/_impl.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sniffio/_impl.py b/sniffio/_impl.py index 07fa1fb..3736c09 100644 --- a/sniffio/_impl.py +++ b/sniffio/_impl.py @@ -77,21 +77,22 @@ async def generic_sleep(seconds): try: asyncio.get_running_loop() return "asyncio" - except AttributeError: - pass - except RuntimeError: - pass - try: - current_task = asyncio.current_task # type: ignore[attr-defined] except AttributeError: - current_task = asyncio.Task.current_task # type: ignore[attr-defined] - try: - if current_task() is not None: - return "asyncio" + try: + current_task = asyncio.current_task # type: ignore[attr-defined] + except AttributeError: + current_task = asyncio.Task.current_task # type: ignore[attr-defined] + try: + if current_task() is not None: + return "asyncio" + except RuntimeError: + pass + except RuntimeError: pass + # Sniff for curio (for now) if 'curio' in sys.modules: from curio.meta import curio_running