Cannot test synchronous consumer that includes async_to_sync
See original GitHub issueI am having a problem calling async_to_sync from python manage.py tests
. The relevant code is
class MyHandler(SyncConsumer):
def websocket_connect(self, event):
self.send({ "type": "websocket.accept"})
async_to_sync(self.channel_layer.group_add('grpup', self.channel_name))
Test looks like this – I use asynctest
because I don’t want this to be the only test in my project that uses pytest
syntax.
class ChannelTests(asynctest.TestCase):
async def test_websockets(self):
communicator = WebsocketCommunicator(MyHandler, path)
connected, subprotocol = await communicator.connect()
self.assertTrue(connected)
When I run this, I get RuntimeError: You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly.
But I’m actually not sure it ever gets to my async_to_sync call. Looking at the call stack, it seems to bomb on the previous line:
Traceback (most recent call last):
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/asynctest/case.py", line 297, in run
self._run_test_method(testMethod)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/asynctest/case.py", line 354, in _run_test_method
self.loop.run_until_complete(result)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/asynctest/case.py", line 224, in wrapper
return method(*args, **kwargs)
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
return future.result()
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/tasks.py", line 241, in _step
result = coro.throw(exc)
File "/Users/jonathanstray/PycharmProjects/cjworkbench/server/tests/test_websockets.py", line 26, in test_websockets
connected, subprotocol = await communicator.connect()
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/channels/testing/websocket.py", line 33, in connect
response = await self.receive_output(timeout)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/asgiref/testing.py", line 66, in receive_output
self.future.result()
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/tasks.py", line 241, in _step
result = coro.throw(exc)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/channels/consumer.py", line 54, in __call__
await await_many_dispatch([receive, self.channel_receive], self.dispatch)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/channels/utils.py", line 48, in await_many_dispatch
await dispatch(result)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/asgiref/sync.py", line 110, in __call__
return await asyncio.wait_for(future, timeout=None)
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/tasks.py", line 373, in wait_for
return (yield from fut)
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/futures.py", line 361, in __iter__
yield self # This tells Task to wait for completion.
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
future.result()
File "/Users/jonathanstray/anaconda/lib/python3.5/asyncio/futures.py", line 274, in result
raise self._exception
File "/Users/jonathanstray/anaconda/lib/python3.5/concurrent/futures/thread.py", line 55, in run
result = self.fn(*self.args, **self.kwargs)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/channels/db.py", line 13, in thread_handler
return super().thread_handler(loop, *args, **kwargs)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/asgiref/sync.py", line 125, in thread_handler
return self.func(*args, **kwargs)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/channels/consumer.py", line 99, in dispatch
handler(message)
File "/Users/jonathanstray/PycharmProjects/cjworkbench/server/websockets.py", line 36, in websocket_connect
self.send({ "type": "websocket.accept"})
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/channels/consumer.py", line 107, in send
self.base_send(message)
File "/Users/jonathanstray/anaconda/lib/python3.5/site-packages/asgiref/sync.py", line 34, in __call__
"You cannot use AsyncToSync in the same thread as an async event loop - "
RuntimeError: You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly.
This is on OS X / django 1.11 / channels 2.0.2
Issue Analytics
- State:
- Created 6 years ago
- Reactions:1
- Comments:15 (5 by maintainers)
Top Results From Across the Web
Django Channels Error: you cannot use AsyncToSync in the ...
You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly. Here is the...
Read more >Asynchronous support - Django documentation
Django has support for writing asynchronous (“async”) views, along with an entirely async-enabled request stack if you are running under ASGI.
Read more >Testing — Channels 4.0.0 documentation
Testing Channels consumers is a little trickier than testing normal Django views due to their underlying asynchronous nature. To help with testing, Channels ......
Read more >Django Channels 2.0.0 groups
This example uses WebSocket consumer, which is synchronous, and so # needs the async channel layer functions to be converted. from asgiref.sync import ......
Read more >6 | Asynchronous chat consumer | Django Channels 2
How to use Async programming to implement asynchronous consumer ?Links - Source Code - https://github.com/aarav-tech/chat_demoFacebook ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Maybe not directly related, but there’s probably a problem with your code. The line
async_to_sync(self.channel_layer.group_add('grpup', self.channel_name))
should beasync_to_sync(self.channel_layer.group_add)('grpup', self.channel_name)
instead. You want to convert thegroup_add
method into a synchronous version and call it. See https://channels.readthedocs.io/en/latest/topics/channel_layers.html#synchronous-functionsIf an async test runs some function which in turn runs something within
async_to_sync
you can wrap that function insync_to_async
to get rid of the “You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly” error.