From 09a28e190aecf17aeaead5820d060bd93d765e10 Mon Sep 17 00:00:00 2001 From: Petter Friberg Date: Tue, 10 May 2022 13:47:45 +0200 Subject: [PATCH 1/2] Don't warn 'non-async-marked callable' for async callable instance --- asgiref/sync.py | 7 ++++++- tests/test_sync.py | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/asgiref/sync.py b/asgiref/sync.py index a70dac18..d02bd4aa 100644 --- a/asgiref/sync.py +++ b/asgiref/sync.py @@ -107,7 +107,12 @@ class AsyncToSync: loop_thread_executors: "Dict[asyncio.AbstractEventLoop, CurrentThreadExecutor]" = {} def __init__(self, awaitable, force_new_loop=False): - if not callable(awaitable) or not _iscoroutinefunction_or_partial(awaitable): + if not callable(awaitable) or ( + not _iscoroutinefunction_or_partial(awaitable) + and not _iscoroutinefunction_or_partial( + getattr(awaitable, "__call__", awaitable) + ) + ): # Python does not have very reliable detection of async functions # (lots of false negatives) so this is just a warning. warnings.warn( diff --git a/tests/test_sync.py b/tests/test_sync.py index 2837423b..8f862fc5 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -365,6 +365,29 @@ async def inner_async_function(*args): assert result["worked"] +def test_async_to_sync_on_callable_object(): + """ + Tests async_to_sync on a callable class instance + """ + + result = {} + + class CallableClass: + async def __call__(self, value): + await asyncio.sleep(0) + result["worked"] = True + return value + + # Run it + with pytest.warns(None) as recorded_warnings: + sync_function = async_to_sync(CallableClass()) + out = sync_function(42) + + assert out == 42 + assert result["worked"] is True + assert len(recorded_warnings) == 0 + + def test_async_to_sync_method_self_attribute(): """ Tests async_to_sync on a method copies __self__. From 734b774cb60069bf4448ce806d8216ae50971521 Mon Sep 17 00:00:00 2001 From: Petter Friberg Date: Tue, 10 May 2022 14:20:38 +0200 Subject: [PATCH 2/2] fixup! Don't warn 'non-async-marked callable' for async callable instance --- tests/test_sync.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_sync.py b/tests/test_sync.py index 8f862fc5..c5677fcb 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -3,6 +3,7 @@ import multiprocessing import threading import time +import warnings from concurrent.futures import ThreadPoolExecutor from functools import wraps from unittest import TestCase @@ -378,14 +379,14 @@ async def __call__(self, value): result["worked"] = True return value - # Run it - with pytest.warns(None) as recorded_warnings: + # Run it (without warnings) + with warnings.catch_warnings(): + warnings.simplefilter("error") sync_function = async_to_sync(CallableClass()) out = sync_function(42) assert out == 42 assert result["worked"] is True - assert len(recorded_warnings) == 0 def test_async_to_sync_method_self_attribute():